2022-05-13 13:12:05 +00:00
|
|
|
// Copyright (C) 2021 The Qt Company Ltd.
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
#include "qqmljscodegenerator_p.h"
|
2022-12-13 00:43:09 +00:00
|
|
|
#include "qqmljsmetatypes_p.h"
|
|
|
|
#include "qqmljsregistercontent_p.h"
|
|
|
|
#include "qqmljsscope_p.h"
|
|
|
|
#include "qqmljsutils_p.h"
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
#include <private/qqmljstypepropagator_p.h>
|
|
|
|
|
|
|
|
#include <private/qqmlirbuilder_p.h>
|
|
|
|
#include <private/qqmljsscope_p.h>
|
2021-12-17 14:52:17 +00:00
|
|
|
#include <private/qqmljsutils_p.h>
|
2021-11-16 15:49:49 +00:00
|
|
|
#include <private/qv4compilerscanfunctions_p.h>
|
2022-02-16 13:01:52 +00:00
|
|
|
#include <private/qduplicatetracker_p.h>
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
#include <QtCore/qdir.h>
|
|
|
|
#include <QtCore/qfileinfo.h>
|
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
using namespace Qt::StringLiterals;
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \class QQmlJSCodeGenerator
|
|
|
|
*
|
|
|
|
* This is a final compile pass that generates C++ code from a function and the
|
|
|
|
* annotations produced by previous passes. Such annotations are produced by
|
|
|
|
* QQmlJSTypePropagator, and possibly amended by other passes.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define BYTECODE_UNIMPLEMENTED() Q_ASSERT_X(false, Q_FUNC_INFO, "not implemented");
|
|
|
|
|
|
|
|
#define INJECT_TRACE_INFO(function) \
|
|
|
|
static const bool injectTraceInfo = true; \
|
|
|
|
if (injectTraceInfo) { \
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"// "_s + QStringLiteral(#function) + u'\n'; \
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2022-11-18 10:40:10 +00:00
|
|
|
|
|
|
|
static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &type)
|
|
|
|
{
|
|
|
|
return !type.isNull()
|
|
|
|
&& !resolver->equals(type, resolver->nullType())
|
|
|
|
&& !resolver->equals(type, resolver->voidType());
|
|
|
|
}
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
QString QQmlJSCodeGenerator::castTargetName(const QQmlJSScope::ConstPtr &type) const
|
|
|
|
{
|
2021-12-17 14:52:17 +00:00
|
|
|
return type->augmentedInternalName();
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QQmlJSCodeGenerator::QQmlJSCodeGenerator(const QV4::Compiler::Context *compilerContext,
|
|
|
|
const QV4::Compiler::JSUnitGenerator *unitGenerator,
|
|
|
|
const QQmlJSTypeResolver *typeResolver,
|
2023-02-22 10:06:54 +00:00
|
|
|
QQmlJSLogger *logger)
|
2021-11-16 15:49:49 +00:00
|
|
|
: QQmlJSCompilePass(unitGenerator, typeResolver, logger)
|
|
|
|
, m_context(compilerContext)
|
|
|
|
{}
|
|
|
|
|
|
|
|
QString QQmlJSCodeGenerator::metaTypeFromType(const QQmlJSScope::ConstPtr &type) const
|
|
|
|
{
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"QMetaType::fromType<"_s + type->augmentedInternalName() + u">()"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QString QQmlJSCodeGenerator::metaTypeFromName(const QQmlJSScope::ConstPtr &type) const
|
|
|
|
{
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"[]() { static const auto t = QMetaType::fromName(\""_s
|
2022-03-16 08:32:24 +00:00
|
|
|
+ QString::fromUtf8(QMetaObject::normalizedType(type->augmentedInternalName().toUtf8()))
|
2022-03-21 09:21:18 +00:00
|
|
|
+ u"\"); return t; }()"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QString QQmlJSCodeGenerator::metaObject(const QQmlJSScope::ConstPtr &objectType)
|
|
|
|
{
|
2022-02-24 15:36:14 +00:00
|
|
|
if (!objectType->isComposite()) {
|
2022-03-21 09:21:18 +00:00
|
|
|
if (objectType->internalName() == u"QObject"_s
|
|
|
|
|| objectType->internalName() == u"QQmlComponent"_s) {
|
|
|
|
return u'&' + objectType->internalName() + u"::staticMetaObject"_s;
|
2022-02-24 15:36:14 +00:00
|
|
|
}
|
2022-03-21 09:21:18 +00:00
|
|
|
return metaTypeFromName(objectType) + u".metaObject()"_s;
|
2022-02-24 15:36:14 +00:00
|
|
|
}
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"retrieving the metaObject of a composite type without using an instance."_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
2023-02-16 09:54:34 +00:00
|
|
|
QString QQmlJSCodeGenerator::metaType(const QQmlJSScope::ConstPtr &type)
|
|
|
|
{
|
|
|
|
return m_typeResolver->equals(m_typeResolver->genericType(type), type)
|
|
|
|
? metaTypeFromType(type)
|
|
|
|
: metaTypeFromName(type);
|
|
|
|
}
|
|
|
|
|
2023-07-11 13:01:10 +00:00
|
|
|
QQmlJSAotFunction QQmlJSCodeGenerator::run(const Function *function,
|
|
|
|
const InstructionAnnotations *annotations,
|
|
|
|
QQmlJS::DiagnosticMessage *error,
|
|
|
|
bool basicBlocksValidationFailed)
|
2021-11-16 15:49:49 +00:00
|
|
|
{
|
|
|
|
m_annotations = annotations;
|
|
|
|
m_function = function;
|
|
|
|
m_error = error;
|
|
|
|
|
2023-07-11 11:42:47 +00:00
|
|
|
QHash<int, int> numRegisterVariablesPerIndex;
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2023-08-14 10:37:42 +00:00
|
|
|
const auto addVariable
|
|
|
|
= [&](int registerIndex, int lookupIndex, const QQmlJSScope::ConstPtr &seenType) {
|
2022-02-02 18:08:29 +00:00
|
|
|
// Don't generate any variables for registers that are initialized with undefined.
|
2022-02-16 13:01:52 +00:00
|
|
|
if (registerIndex == InvalidRegister || !isTypeStorable(m_typeResolver, seenType))
|
2022-02-02 18:08:29 +00:00
|
|
|
return;
|
|
|
|
|
2023-08-14 10:37:42 +00:00
|
|
|
const RegisterVariablesKey key = { seenType->internalName(), registerIndex, lookupIndex };
|
2023-07-11 11:42:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
const auto oldSize = m_registerVariables.size();
|
|
|
|
auto &e = m_registerVariables[key];
|
|
|
|
if (m_registerVariables.size() != oldSize) {
|
|
|
|
e.variableName = u"r%1_%2"_s
|
|
|
|
.arg(registerIndex)
|
|
|
|
.arg(numRegisterVariablesPerIndex[registerIndex]++);
|
|
|
|
e.storedType = m_typeResolver->comparableType(seenType);
|
2022-02-02 18:08:29 +00:00
|
|
|
}
|
2023-07-11 11:42:47 +00:00
|
|
|
++e.numTracked;
|
2022-02-02 18:08:29 +00:00
|
|
|
};
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-03-01 11:30:24 +00:00
|
|
|
QT_WARNING_PUSH
|
|
|
|
QT_WARNING_DISABLE_CLANG("-Wrange-loop-analysis")
|
2022-02-16 14:12:26 +00:00
|
|
|
for (const auto &annotation : *m_annotations) {
|
|
|
|
addVariable(annotation.second.changedRegisterIndex,
|
2023-08-14 10:37:42 +00:00
|
|
|
annotation.second.changedRegister.resultLookupIndex(),
|
2022-02-16 14:12:26 +00:00
|
|
|
annotation.second.changedRegister.storedType());
|
|
|
|
for (auto it = annotation.second.typeConversions.begin(),
|
|
|
|
end = annotation.second.typeConversions.end();
|
2022-02-02 18:08:29 +00:00
|
|
|
it != end; ++it) {
|
2023-08-14 10:37:42 +00:00
|
|
|
addVariable(
|
|
|
|
it.key(), it.value().content.resultLookupIndex(),
|
|
|
|
it.value().content.storedType());
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
}
|
2022-03-01 11:30:24 +00:00
|
|
|
QT_WARNING_POP
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
// ensure we have m_labels for loops
|
|
|
|
for (const auto loopLabel : m_context->labelInfo)
|
2022-10-05 05:29:16 +00:00
|
|
|
m_labels.insert(loopLabel, u"label_%1"_s.arg(m_labels.size()));
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-06-28 20:04:43 +00:00
|
|
|
// Initialize the first instruction's state to hold the arguments.
|
|
|
|
// After this, the arguments (or whatever becomes of them) are carried
|
|
|
|
// over into any further basic blocks automatically.
|
|
|
|
m_state.State::operator=(initialState(m_function));
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
const QByteArray byteCode = function->code;
|
2022-10-05 05:29:16 +00:00
|
|
|
decode(byteCode.constData(), static_cast<uint>(byteCode.size()));
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
QQmlJSAotFunction result;
|
|
|
|
result.includes.swap(m_includes);
|
|
|
|
|
2023-07-11 13:01:10 +00:00
|
|
|
if (basicBlocksValidationFailed) {
|
|
|
|
result.code += "// QV4_BASIC_BLOCK_VALIDATION_FAILED: This file failed compilation "_L1
|
|
|
|
"with basic blocks validation but compiled without it.\n"_L1;
|
|
|
|
}
|
|
|
|
|
2023-02-22 10:06:54 +00:00
|
|
|
result.code += u"// %1 at line %2, column %3\n"_s
|
|
|
|
.arg(m_context->name).arg(m_context->line).arg(m_context->column);
|
|
|
|
|
2022-06-27 14:26:43 +00:00
|
|
|
for (auto registerIt = m_registerVariables.cbegin(), registerEnd = m_registerVariables.cend();
|
|
|
|
registerIt != registerEnd; ++registerIt) {
|
|
|
|
|
2023-07-11 11:42:47 +00:00
|
|
|
const int registerIndex = registerIt.key().registerIndex;
|
2022-06-27 14:26:43 +00:00
|
|
|
const bool registerIsArgument = isArgument(registerIndex);
|
|
|
|
|
2023-07-11 11:42:47 +00:00
|
|
|
result.code += registerIt.key().internalName;
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2023-07-11 11:42:47 +00:00
|
|
|
const QQmlJSScope::ConstPtr storedType = registerIt->storedType;
|
|
|
|
const bool isPointer
|
|
|
|
= (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
|
|
|
|
if (isPointer)
|
|
|
|
result.code += u" *"_s;
|
|
|
|
else
|
|
|
|
result.code += u' ';
|
|
|
|
|
|
|
|
if (!registerIsArgument
|
|
|
|
&& registerIndex != Accumulator
|
|
|
|
&& registerIndex != This
|
|
|
|
&& !m_typeResolver->registerIsStoredIn(
|
|
|
|
function->registerTypes[registerIndex - firstRegisterIndex()],
|
|
|
|
m_typeResolver->voidType())) {
|
|
|
|
result.code += registerIt->variableName + u" = "_s;
|
|
|
|
result.code += convertStored(m_typeResolver->voidType(), storedType, QString());
|
|
|
|
} else if (registerIsArgument && m_typeResolver->registerIsStoredIn(
|
|
|
|
argumentType(registerIndex), storedType)) {
|
|
|
|
const int argumentIndex = registerIndex - FirstArgument;
|
|
|
|
const QQmlJSRegisterContent argument
|
|
|
|
= m_function->argumentTypes[argumentIndex];
|
|
|
|
const QQmlJSRegisterContent original
|
|
|
|
= m_typeResolver->original(argument);
|
|
|
|
|
|
|
|
const bool needsConversion = argument != original;
|
|
|
|
if (!isPointer && registerIt->numTracked == 1 && !needsConversion) {
|
|
|
|
// Not a pointer, never written to, and doesn't need any initial conversion.
|
|
|
|
// This is a readonly argument.
|
|
|
|
//
|
|
|
|
// We would like to make the variable a const ref if it's a readonly argument,
|
|
|
|
// but due to the various call interfaces accepting non-const values, we can't.
|
|
|
|
// We rely on those calls to still not modify their arguments in place.
|
|
|
|
result.code += u'&';
|
|
|
|
}
|
2022-02-16 13:01:52 +00:00
|
|
|
|
2023-07-11 11:42:47 +00:00
|
|
|
result.code += registerIt->variableName + u" = "_s;
|
2022-02-03 16:31:33 +00:00
|
|
|
|
2023-11-21 16:44:26 +00:00
|
|
|
const QString originalValue
|
|
|
|
= u"(*static_cast<"_s + castTargetName(original.storedType())
|
|
|
|
+ u"*>(argumentsPtr["_s + QString::number(argumentIndex) + u"]))"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2023-07-11 11:42:47 +00:00
|
|
|
if (needsConversion)
|
|
|
|
result.code += conversion(original, argument, originalValue);
|
2021-11-16 15:49:49 +00:00
|
|
|
else
|
2023-07-11 11:42:47 +00:00
|
|
|
result.code += originalValue;
|
|
|
|
} else {
|
|
|
|
result.code += registerIt->variableName;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
2023-07-11 11:42:47 +00:00
|
|
|
result.code += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2022-02-17 12:04:35 +00:00
|
|
|
result.code += m_body;
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-10-06 09:30:50 +00:00
|
|
|
for (const QQmlJSRegisterContent &argType : std::as_const(function->argumentTypes)) {
|
2022-02-21 16:35:44 +00:00
|
|
|
if (argType.isValid()) {
|
2022-06-29 12:01:22 +00:00
|
|
|
result.argumentTypes.append(
|
|
|
|
m_typeResolver->originalType(argType.storedType())
|
|
|
|
->augmentedInternalName());
|
2021-11-16 15:49:49 +00:00
|
|
|
} else {
|
2022-03-21 09:21:18 +00:00
|
|
|
result.argumentTypes.append(u"void"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (function->returnType) {
|
|
|
|
result.returnType = function->returnType->internalName();
|
|
|
|
if (function->returnType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
|
|
|
|
result.returnType += u'*';
|
|
|
|
} else {
|
2022-03-21 09:21:18 +00:00
|
|
|
result.returnType = u"void"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-02-03 16:31:33 +00:00
|
|
|
QString QQmlJSCodeGenerator::errorReturnValue()
|
2021-11-16 15:49:49 +00:00
|
|
|
{
|
2022-06-20 09:19:58 +00:00
|
|
|
if (auto ret = m_function->returnType) {
|
|
|
|
return ret->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
|
2023-02-21 10:47:08 +00:00
|
|
|
? convertStored(m_typeResolver->nullType(), ret, QString())
|
2022-06-20 09:19:58 +00:00
|
|
|
: ret->internalName() + u"()"_s;
|
|
|
|
}
|
2021-11-16 15:49:49 +00:00
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_Ret()
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_Ret);
|
|
|
|
|
|
|
|
if (m_function->returnType) {
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
if (!m_state.accumulatorVariableIn.isEmpty()) {
|
2022-02-17 12:04:35 +00:00
|
|
|
const QString in = m_state.accumulatorVariableIn;
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->registerIsStoredIn(
|
|
|
|
m_state.accumulatorIn(), m_typeResolver->varType())) {
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"if (!"_s + in + u".isValid())\n"_s;
|
|
|
|
m_body += u" "_s + signalUndefined;
|
2022-02-04 21:35:23 +00:00
|
|
|
} else if (m_typeResolver->registerIsStoredIn(
|
|
|
|
m_state.accumulatorIn(), m_typeResolver->jsPrimitiveType())) {
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"if ("_s + in
|
|
|
|
+ u".type() == QJSPrimitiveValue::Undefined)\n"_s;
|
|
|
|
m_body += u" "_s + signalUndefined;
|
2022-02-04 21:35:23 +00:00
|
|
|
} else if (m_typeResolver->registerIsStoredIn(
|
|
|
|
m_state.accumulatorIn(), m_typeResolver->jsValueType())) {
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"if ("_s + in + u".isUndefined())\n"_s;
|
|
|
|
m_body += u" "_s + signalUndefined;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"return "_s
|
2023-02-21 10:47:08 +00:00
|
|
|
+ convertStored(m_state.accumulatorIn().storedType(), m_function->returnType, in);
|
2022-02-16 13:01:52 +00:00
|
|
|
} else {
|
|
|
|
if (m_typeResolver->equals(m_state.accumulatorIn().storedType(),
|
|
|
|
m_typeResolver->voidType())) {
|
|
|
|
m_body += signalUndefined;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
2023-02-21 10:47:08 +00:00
|
|
|
m_body += u"return "_s + convertStored(
|
2022-02-16 13:01:52 +00:00
|
|
|
m_state.accumulatorIn().storedType(), m_function->returnType, QString());
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
} else {
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"return"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
m_skipUntilNextLabel = true;
|
2022-06-27 14:26:43 +00:00
|
|
|
resetState();
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_Debug()
|
|
|
|
{
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
static QString toNumericString(double value)
|
|
|
|
{
|
|
|
|
if (value >= std::numeric_limits<int>::min() && value <= std::numeric_limits<int>::max()) {
|
|
|
|
const int i = value;
|
|
|
|
if (i == value)
|
|
|
|
return QString::number(i);
|
|
|
|
}
|
|
|
|
|
2022-02-17 09:02:51 +00:00
|
|
|
switch (qFpClassify(value)) {
|
|
|
|
case FP_INFINITE: {
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString inf = u"std::numeric_limits<double>::infinity()"_s;
|
2022-02-17 09:02:51 +00:00
|
|
|
return std::signbit(value) ? (u'-' + inf) : inf;
|
|
|
|
}
|
|
|
|
case FP_NAN:
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"std::numeric_limits<double>::quiet_NaN()"_s;
|
2022-02-17 09:02:51 +00:00
|
|
|
case FP_ZERO:
|
2022-03-21 09:21:18 +00:00
|
|
|
return std::signbit(value) ? u"-0.0"_s : u"0"_s;
|
2022-02-17 09:02:51 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
return QString::number(value, 'f', std::numeric_limits<double>::max_digits10);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadConst(int index)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_LoadConst);
|
|
|
|
|
2022-07-29 14:13:43 +00:00
|
|
|
// You cannot actually get it to generate LoadConst for anything but double. We have
|
|
|
|
// a numer of specialized instructions for the other types, after all. However, let's
|
|
|
|
// play it safe.
|
|
|
|
|
|
|
|
const QV4::ReturnedValue encodedConst = m_jsUnitGenerator->constant(index);
|
|
|
|
const QV4::StaticValue value = QV4::StaticValue::fromReturnedValue(encodedConst);
|
|
|
|
const QQmlJSScope::ConstPtr type = m_typeResolver->typeForConst(encodedConst);
|
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s;
|
|
|
|
if (type == m_typeResolver->realType()) {
|
|
|
|
m_body += conversion(
|
2023-02-21 10:47:08 +00:00
|
|
|
type, m_state.accumulatorOut(),
|
2022-07-29 14:13:43 +00:00
|
|
|
toNumericString(value.doubleValue()));
|
2023-03-27 16:00:46 +00:00
|
|
|
} else if (type == m_typeResolver->int32Type()) {
|
2022-07-29 14:13:43 +00:00
|
|
|
m_body += conversion(
|
2023-02-21 10:47:08 +00:00
|
|
|
type, m_state.accumulatorOut(),
|
2022-07-29 14:13:43 +00:00
|
|
|
QString::number(value.integerValue()));
|
|
|
|
} else if (type == m_typeResolver->boolType()) {
|
|
|
|
m_body += conversion(
|
2023-02-21 10:47:08 +00:00
|
|
|
type, m_state.accumulatorOut(),
|
2022-07-29 14:13:43 +00:00
|
|
|
value.booleanValue() ? u"true"_s : u"false"_s);
|
|
|
|
} else if (type == m_typeResolver->voidType()) {
|
|
|
|
m_body += conversion(
|
2023-02-21 10:47:08 +00:00
|
|
|
type, m_state.accumulatorOut(),
|
2022-07-29 14:13:43 +00:00
|
|
|
QString());
|
|
|
|
} else if (type == m_typeResolver->nullType()) {
|
|
|
|
m_body += conversion(
|
2023-02-21 10:47:08 +00:00
|
|
|
type, m_state.accumulatorOut(),
|
2022-07-29 14:13:43 +00:00
|
|
|
u"nullptr"_s);
|
|
|
|
} else {
|
|
|
|
reject(u"unsupported constant type"_s);
|
|
|
|
}
|
2022-02-16 15:34:56 +00:00
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadZero()
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_LoadZero);
|
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut;
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u" = "_s + conversion(
|
2023-03-27 16:00:46 +00:00
|
|
|
m_typeResolver->int32Type(), m_state.accumulatorOut(), u"0"_s);
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadTrue()
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_LoadTrue);
|
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut;
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u" = "_s + conversion(
|
2023-02-21 10:47:08 +00:00
|
|
|
m_typeResolver->boolType(), m_state.accumulatorOut(), u"true"_s);
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadFalse()
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_LoadFalse);
|
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut;
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u" = "_s + conversion(
|
2023-02-21 10:47:08 +00:00
|
|
|
m_typeResolver->boolType(), m_state.accumulatorOut(), u"false"_s);
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadNull()
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_LoadNull);
|
2022-02-16 13:01:52 +00:00
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s;
|
2023-02-21 10:47:08 +00:00
|
|
|
m_body += conversion(m_typeResolver->nullType(), m_state.accumulatorOut(),
|
2022-03-21 09:21:18 +00:00
|
|
|
u"nullptr"_s);
|
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadUndefined()
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_LoadUndefined);
|
2022-02-16 13:01:52 +00:00
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s;
|
2023-02-21 10:47:08 +00:00
|
|
|
m_body += conversion(m_typeResolver->voidType(), m_state.accumulatorOut(),
|
2022-02-16 13:01:52 +00:00
|
|
|
QString());
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadInt(int value)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_LoadInt);
|
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut;
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u" = "_s;
|
2023-03-27 16:00:46 +00:00
|
|
|
m_body += conversion(m_typeResolver->int32Type(), m_state.accumulatorOut(),
|
2022-02-16 13:01:52 +00:00
|
|
|
QString::number(value));
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_MoveConst);
|
|
|
|
|
2022-02-02 18:08:29 +00:00
|
|
|
Q_ASSERT(destTemp == m_state.changedRegisterIndex());
|
|
|
|
|
|
|
|
auto var = changedRegisterVariable();
|
2021-11-16 15:49:49 +00:00
|
|
|
if (var.isEmpty())
|
|
|
|
return; // Do not load 'undefined'
|
|
|
|
|
|
|
|
const auto v4Value = QV4::StaticValue::fromReturnedValue(
|
|
|
|
m_jsUnitGenerator->constant(constIndex));
|
|
|
|
|
2023-02-21 10:47:08 +00:00
|
|
|
const auto changed = m_state.changedRegister();
|
2022-02-16 15:34:56 +00:00
|
|
|
QQmlJSScope::ConstPtr contained;
|
|
|
|
QString input;
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += var + u" = "_s;
|
2022-02-24 07:53:07 +00:00
|
|
|
if (v4Value.isNull()) {
|
2022-02-16 15:34:56 +00:00
|
|
|
contained = m_typeResolver->nullType();
|
2022-02-24 07:53:07 +00:00
|
|
|
} else if (v4Value.isUndefined()) {
|
2022-02-16 15:34:56 +00:00
|
|
|
contained = m_typeResolver->voidType();
|
2022-02-24 07:53:07 +00:00
|
|
|
} else if (v4Value.isBoolean()) {
|
2022-02-16 15:34:56 +00:00
|
|
|
contained = m_typeResolver->boolType();
|
2022-03-21 09:21:18 +00:00
|
|
|
input = v4Value.booleanValue() ? u"true"_s : u"false"_s;
|
2022-02-24 07:53:07 +00:00
|
|
|
} else if (v4Value.isInteger()) {
|
2023-03-27 16:00:46 +00:00
|
|
|
contained = m_typeResolver->int32Type();
|
2022-02-16 15:34:56 +00:00
|
|
|
input = QString::number(v4Value.int_32());
|
2022-02-24 07:53:07 +00:00
|
|
|
} else if (v4Value.isDouble()) {
|
2022-02-16 15:34:56 +00:00
|
|
|
contained = m_typeResolver->realType();
|
|
|
|
input = toNumericString(v4Value.doubleValue());
|
2022-02-24 07:53:07 +00:00
|
|
|
} else {
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"unknown const type"_s);
|
2023-11-17 08:35:35 +00:00
|
|
|
return;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += conversion(contained, changed, input) + u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadReg(int reg)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_LoadReg);
|
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut;
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u" = "_s;
|
2022-11-28 15:07:24 +00:00
|
|
|
m_body += conversion(
|
|
|
|
registerType(reg), m_state.accumulatorOut(), consumedRegisterVariable(reg));
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_StoreReg(int reg)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_StoreReg);
|
|
|
|
|
2022-02-02 18:08:29 +00:00
|
|
|
Q_ASSERT(m_state.changedRegisterIndex() == reg);
|
|
|
|
Q_ASSERT(m_state.accumulatorIn().isValid());
|
|
|
|
const QString var = changedRegisterVariable();
|
2021-11-16 15:49:49 +00:00
|
|
|
if (var.isEmpty())
|
|
|
|
return; // don't store "undefined"
|
|
|
|
m_body += var;
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u" = "_s;
|
2022-02-02 18:08:29 +00:00
|
|
|
m_body += conversion(m_state.accumulatorIn(), m_state.changedRegister(),
|
2022-11-28 15:07:24 +00:00
|
|
|
consumedAccumulatorVariableIn());
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_MoveReg(int srcReg, int destReg)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_MoveReg);
|
|
|
|
|
2022-02-02 18:08:29 +00:00
|
|
|
Q_ASSERT(m_state.changedRegisterIndex() == destReg);
|
|
|
|
const QString destRegName = changedRegisterVariable();
|
2022-03-01 07:44:28 +00:00
|
|
|
if (destRegName.isEmpty())
|
|
|
|
return; // don't store things we cannot store.
|
2021-11-16 15:49:49 +00:00
|
|
|
m_body += destRegName;
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u" = "_s;
|
2022-11-28 15:07:24 +00:00
|
|
|
m_body += conversion(
|
|
|
|
registerType(srcReg), m_state.changedRegister(), consumedRegisterVariable(srcReg));
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadImport(int index)
|
|
|
|
{
|
|
|
|
Q_UNUSED(index)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadLocal(int index)
|
|
|
|
{
|
|
|
|
Q_UNUSED(index);
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"LoadLocal"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_StoreLocal(int index)
|
|
|
|
{
|
|
|
|
Q_UNUSED(index)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadScopedLocal(int scope, int index)
|
|
|
|
{
|
|
|
|
Q_UNUSED(scope)
|
|
|
|
Q_UNUSED(index)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_StoreScopedLocal(int scope, int index)
|
|
|
|
{
|
|
|
|
Q_UNUSED(scope)
|
|
|
|
Q_UNUSED(index)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadRuntimeString(int stringId)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_LoadRuntimeString);
|
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut;
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u" = "_s;
|
2023-02-21 10:47:08 +00:00
|
|
|
m_body += conversion(m_typeResolver->stringType(), m_state.accumulatorOut(),
|
2022-02-16 13:01:52 +00:00
|
|
|
QQmlJSUtils::toLiteral(m_jsUnitGenerator->stringForIndex(stringId)));
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_MoveRegExp(int regExpId, int destReg)
|
|
|
|
{
|
|
|
|
Q_UNUSED(regExpId)
|
|
|
|
Q_UNUSED(destReg)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadClosure(int value)
|
|
|
|
{
|
|
|
|
Q_UNUSED(value)
|
2022-05-18 12:39:52 +00:00
|
|
|
reject(u"LoadClosure"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadName(int nameIndex)
|
|
|
|
{
|
|
|
|
Q_UNUSED(nameIndex)
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"LoadName"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadGlobalLookup(int index)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_LoadGlobalLookup);
|
|
|
|
|
2022-02-16 15:34:56 +00:00
|
|
|
AccumulatorConverter registers(this);
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString lookup = u"aotContext->loadGlobalLookup("_s + QString::number(index)
|
|
|
|
+ u", &"_s + m_state.accumulatorVariableOut + u", "_s
|
2022-02-02 18:08:29 +00:00
|
|
|
+ metaTypeFromType(m_state.accumulatorOut().storedType()) + u')';
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString initialization = u"aotContext->initLoadGlobalLookup("_s
|
2021-11-16 15:49:49 +00:00
|
|
|
+ QString::number(index) + u')';
|
|
|
|
generateLookup(lookup, initialization);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadQmlContextPropertyLookup(int index)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_LoadQmlContextPropertyLookup);
|
|
|
|
|
2022-02-16 13:01:52 +00:00
|
|
|
AccumulatorConverter registers(this);
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
|
|
|
|
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
|
2022-02-02 18:08:29 +00:00
|
|
|
if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptGlobal) {
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s
|
2021-11-16 15:49:49 +00:00
|
|
|
+ conversion(
|
2023-02-21 10:47:08 +00:00
|
|
|
m_typeResolver->original(m_state.accumulatorOut()), m_state.accumulatorOut(),
|
2022-03-21 09:21:18 +00:00
|
|
|
u"aotContext->javaScriptGlobalProperty("_s + QString::number(nameIndex) + u")")
|
|
|
|
+ u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QString indexString = QString::number(index);
|
2022-02-02 18:08:29 +00:00
|
|
|
if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectById) {
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString lookup = u"aotContext->loadContextIdLookup("_s
|
|
|
|
+ indexString + u", "_s
|
2022-02-02 18:08:29 +00:00
|
|
|
+ contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString initialization = u"aotContext->initLoadContextIdLookup("_s
|
2021-11-16 15:49:49 +00:00
|
|
|
+ indexString + u')';
|
|
|
|
generateLookup(lookup, initialization);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-02-02 18:08:29 +00:00
|
|
|
const bool isProperty = m_state.accumulatorOut().isProperty();
|
|
|
|
const QQmlJSScope::ConstPtr scope = m_state.accumulatorOut().scopeType();
|
|
|
|
const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
|
2021-11-16 15:49:49 +00:00
|
|
|
if (isProperty) {
|
2022-02-02 18:08:29 +00:00
|
|
|
const auto lookupType = contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut);
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString lookup = u"aotContext->loadScopeObjectPropertyLookup("_s
|
|
|
|
+ indexString + u", "_s
|
2022-02-02 18:08:29 +00:00
|
|
|
+ contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
|
2021-11-16 15:49:49 +00:00
|
|
|
const QString initialization
|
2022-03-21 09:21:18 +00:00
|
|
|
= u"aotContext->initLoadScopeObjectPropertyLookup("_s
|
|
|
|
+ indexString + u", "_s
|
2021-11-16 15:49:49 +00:00
|
|
|
+ lookupType + u')';
|
|
|
|
const QString preparation = getLookupPreparation(
|
2022-02-02 18:08:29 +00:00
|
|
|
m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
generateLookup(lookup, initialization, preparation);
|
2022-02-02 18:08:29 +00:00
|
|
|
} else if (m_state.accumulatorOut().isType() || m_state.accumulatorOut().isImportNamespace()) {
|
2021-11-16 15:49:49 +00:00
|
|
|
generateTypeLookup(index);
|
|
|
|
} else {
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"lookup of %1"_s.arg(m_state.accumulatorOut().descriptiveName()));
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_StoreNameSloppy(int nameIndex)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_StoreNameSloppy);
|
|
|
|
|
|
|
|
const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
|
2023-02-24 12:00:33 +00:00
|
|
|
const QQmlJSRegisterContent type = m_typeResolver->scopedType(m_function->qmlScope, name);
|
2021-11-16 15:49:49 +00:00
|
|
|
Q_ASSERT(type.isProperty());
|
|
|
|
|
|
|
|
switch (type.variant()) {
|
|
|
|
case QQmlJSRegisterContent::ScopeProperty:
|
|
|
|
case QQmlJSRegisterContent::ExtensionScopeProperty: {
|
2022-06-24 12:40:02 +00:00
|
|
|
// Do not convert here. We may intentionally pass the "wrong" type, for example to trigger
|
|
|
|
// a property reset.
|
|
|
|
m_body += u"aotContext->storeNameSloppy("_s + QString::number(nameIndex)
|
|
|
|
+ u", "_s
|
|
|
|
+ contentPointer(m_state.accumulatorIn(), m_state.accumulatorVariableIn)
|
|
|
|
+ u", "_s
|
|
|
|
+ contentType(m_state.accumulatorIn(), m_state.accumulatorVariableIn) + u')';
|
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QQmlJSRegisterContent::ScopeMethod:
|
|
|
|
case QQmlJSRegisterContent::ExtensionScopeMethod:
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"assignment to scope method"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_StoreNameStrict(int name)
|
|
|
|
{
|
|
|
|
Q_UNUSED(name)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadElement(int base)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_LoadElement);
|
|
|
|
|
|
|
|
const QQmlJSRegisterContent baseType = registerType(base);
|
|
|
|
|
2023-09-12 10:16:47 +00:00
|
|
|
if (!baseType.isList()
|
|
|
|
&& !m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->stringType())) {
|
|
|
|
reject(u"LoadElement with non-list base type "_s + baseType.descriptiveName());
|
2021-11-16 15:49:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-05-17 12:40:00 +00:00
|
|
|
const QString voidAssignment = u" "_s + m_state.accumulatorVariableOut + u" = "_s +
|
|
|
|
conversion(m_typeResolver->globalType(m_typeResolver->voidType()),
|
|
|
|
m_state.accumulatorOut(), QString()) + u";\n"_s;
|
|
|
|
|
2023-09-12 10:16:47 +00:00
|
|
|
QString indexName = m_state.accumulatorVariableIn;
|
|
|
|
QQmlJSScope::ConstPtr indexType;
|
|
|
|
if (m_typeResolver->isNumeric(m_state.accumulatorIn())) {
|
|
|
|
indexType = m_typeResolver->containedType(m_state.accumulatorIn());
|
|
|
|
} else if (m_state.accumulatorIn().isConversion()) {
|
2023-11-02 13:46:45 +00:00
|
|
|
const auto target = m_typeResolver->extractNonVoidFromOptionalType(m_state.accumulatorIn());
|
|
|
|
if (m_typeResolver->isNumeric(target)) {
|
|
|
|
indexType = target;
|
|
|
|
m_body += u"if (!" + indexName + u".metaType().isValid())\n"
|
|
|
|
+ voidAssignment
|
|
|
|
+ u"else ";
|
|
|
|
indexName = convertStored(
|
|
|
|
m_state.accumulatorIn().storedType(), indexType, indexName);
|
2023-09-12 10:16:47 +00:00
|
|
|
} else {
|
|
|
|
reject(u"LoadElement with non-numeric argument"_s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
AccumulatorConverter registers(this);
|
|
|
|
const QString baseName = registerVariable(base);
|
|
|
|
|
|
|
|
if (!m_typeResolver->isIntegral(indexType)) {
|
2023-07-28 14:30:45 +00:00
|
|
|
m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u"))\n"_s
|
2022-05-17 12:40:00 +00:00
|
|
|
+ voidAssignment
|
|
|
|
+ u"else "_s;
|
|
|
|
}
|
2022-05-19 13:50:31 +00:00
|
|
|
|
2023-09-12 10:16:47 +00:00
|
|
|
if (!m_typeResolver->isUnsignedInteger(indexType)) {
|
2023-03-27 16:00:46 +00:00
|
|
|
m_body += u"if ("_s + indexName + u" < 0)\n"_s
|
|
|
|
+ voidAssignment
|
|
|
|
+ u"else "_s;
|
|
|
|
}
|
|
|
|
|
2022-05-19 13:50:31 +00:00
|
|
|
if (m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) {
|
|
|
|
// Our QQmlListProperty only keeps plain QObject*.
|
2023-07-26 08:49:39 +00:00
|
|
|
const auto elementType = m_typeResolver->globalType(m_typeResolver->qObjectType());
|
2022-05-19 13:50:31 +00:00
|
|
|
|
2023-03-27 16:00:46 +00:00
|
|
|
m_body += u"if ("_s + indexName + u" < "_s + baseName
|
|
|
|
+ u".count(&"_s + baseName + u"))\n"_s;
|
2022-05-19 13:50:31 +00:00
|
|
|
m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
|
|
|
|
conversion(elementType, m_state.accumulatorOut(),
|
|
|
|
baseName + u".at(&"_s + baseName + u", "_s
|
|
|
|
+ indexName + u')') + u";\n"_s;
|
|
|
|
m_body += u"else\n"_s
|
|
|
|
+ voidAssignment;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto elementType = m_typeResolver->valueType(baseType);
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-05-19 14:38:55 +00:00
|
|
|
QString access = baseName + u".at("_s + indexName + u')';
|
|
|
|
|
|
|
|
// TODO: Once we get a char type in QML, use it here.
|
|
|
|
if (m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->stringType()))
|
|
|
|
access = u"QString("_s + access + u")"_s;
|
2023-04-28 12:02:20 +00:00
|
|
|
else if (m_state.isRegisterAffectedBySideEffects(base))
|
|
|
|
reject(u"LoadElement on a sequence potentially affected by side effects"_s);
|
2023-05-09 08:47:06 +00:00
|
|
|
else if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
|
|
|
|
reject(u"LoadElement on a sequence wrapped in a non-sequence type"_s);
|
2022-05-19 14:38:55 +00:00
|
|
|
|
2023-03-27 16:00:46 +00:00
|
|
|
m_body += u"if ("_s + indexName + u" < "_s + baseName + u".size())\n"_s;
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
|
2022-05-19 14:38:55 +00:00
|
|
|
conversion(elementType, m_state.accumulatorOut(), access) + u";\n"_s;
|
2022-05-17 12:40:00 +00:00
|
|
|
m_body += u"else\n"_s
|
|
|
|
+ voidAssignment;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_StoreElement);
|
|
|
|
|
|
|
|
const QQmlJSRegisterContent baseType = registerType(base);
|
2022-05-17 12:40:00 +00:00
|
|
|
const QQmlJSRegisterContent indexType = registerType(index);
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
if (!m_typeResolver->isNumeric(registerType(index)) || !baseType.isList()) {
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"StoreElement with non-list base type or non-numeric arguments"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-23 15:00:25 +00:00
|
|
|
if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
|
2023-04-28 12:02:20 +00:00
|
|
|
reject(u"indirect StoreElement"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-02-17 12:04:35 +00:00
|
|
|
const QString baseName = registerVariable(base);
|
|
|
|
const QString indexName = registerVariable(index);
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
const auto valueType = m_typeResolver->valueType(baseType);
|
|
|
|
const auto elementType = m_typeResolver->globalType(m_typeResolver->genericType(
|
|
|
|
m_typeResolver->containedType(valueType)));
|
|
|
|
|
2023-11-03 08:45:46 +00:00
|
|
|
addInclude(u"QtQml/qjslist.h"_s);
|
2022-05-17 12:40:00 +00:00
|
|
|
m_body += u"if ("_s;
|
|
|
|
if (!m_typeResolver->isIntegral(indexType))
|
2023-07-28 14:30:45 +00:00
|
|
|
m_body += u"QJSNumberCoercion::isArrayIndex("_s + indexName + u") && "_s;
|
2023-03-27 16:00:46 +00:00
|
|
|
if (!m_typeResolver->isUnsignedInteger(m_typeResolver->containedType(indexType)))
|
2023-08-23 15:00:25 +00:00
|
|
|
m_body += indexName + u" >= 0"_s;
|
|
|
|
|
|
|
|
if (m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) {
|
|
|
|
m_body += u" && "_s + indexName + u" < "_s + baseName + u".count(&"_s + baseName
|
|
|
|
+ u"))\n"_s;
|
|
|
|
m_body += u" "_s + baseName + u".replace(&"_s + baseName
|
|
|
|
+ u", "_s + indexName + u", "_s;
|
|
|
|
m_body += conversion(m_state.accumulatorIn(), elementType, m_state.accumulatorVariableIn)
|
|
|
|
+ u");\n"_s;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_state.isRegisterAffectedBySideEffects(base))
|
|
|
|
reject(u"LoadElement on a sequence potentially affected by side effects"_s);
|
|
|
|
|
|
|
|
m_body += u") {\n"_s;
|
|
|
|
m_body += u" if ("_s + indexName + u" >= " + baseName + u".size())\n"_s;
|
|
|
|
m_body += u" QJSList(&"_s + baseName + u", aotContext->engine).resize("_s
|
|
|
|
+ indexName + u" + 1);\n"_s;
|
|
|
|
m_body += u" "_s + baseName + u'[' + indexName + u"] = "_s;
|
2022-02-17 12:04:35 +00:00
|
|
|
m_body += conversion(m_state.accumulatorIn(), elementType, m_state.accumulatorVariableIn)
|
2023-08-23 15:00:25 +00:00
|
|
|
+ u";\n"_s;
|
|
|
|
m_body += u"}\n"_s;
|
|
|
|
|
|
|
|
generateWriteBack(base);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadProperty(int nameIndex)
|
|
|
|
{
|
|
|
|
Q_UNUSED(nameIndex)
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"LoadProperty"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadOptionalProperty(int name, int offset)
|
|
|
|
{
|
|
|
|
Q_UNUSED(name)
|
|
|
|
Q_UNUSED(offset)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generateEnumLookup(int index)
|
|
|
|
{
|
2022-02-02 18:08:29 +00:00
|
|
|
const QString enumMember = m_state.accumulatorOut().enumMember();
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2023-11-17 09:30:34 +00:00
|
|
|
if (enumMember.isEmpty()) {
|
|
|
|
// If we're referring to the type, there's nothing to do.
|
|
|
|
// However, we should not get here since no one can ever use the enum metatype.
|
|
|
|
// The lookup is dead code and should be optimized away.
|
|
|
|
// ... unless you are actually trying to store the metatype itself in a property.
|
|
|
|
// We cannot compile such code.
|
|
|
|
reject(u"Lookup of enum metatype"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
return;
|
2023-11-17 09:30:34 +00:00
|
|
|
}
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
// If the metaenum has the value, just use it and skip all the rest.
|
2022-02-02 18:08:29 +00:00
|
|
|
const QQmlJSMetaEnum metaEnum = m_state.accumulatorOut().enumeration();
|
2021-11-16 15:49:49 +00:00
|
|
|
if (metaEnum.hasValues()) {
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s
|
2021-11-16 15:49:49 +00:00
|
|
|
+ QString::number(metaEnum.value(enumMember));
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-02-02 18:08:29 +00:00
|
|
|
const QQmlJSScope::ConstPtr scopeType = m_state.accumulatorOut().scopeType();
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
// Otherwise we would have found an enum with values.
|
|
|
|
Q_ASSERT(!scopeType->isComposite());
|
|
|
|
|
|
|
|
const QString enumName = metaEnum.isFlag() ? metaEnum.alias() : metaEnum.name();
|
2023-06-23 09:50:07 +00:00
|
|
|
if (enumName.isEmpty()) {
|
|
|
|
if (metaEnum.isFlag() && !metaEnum.name().isEmpty())
|
|
|
|
reject(u"qmltypes misses name entry for flag; did you pass the enum type to Q_FLAG instead of the QFlag type?"
|
|
|
|
"\nType is %1, enum name is %2"_s.arg(scopeType->internalName(), metaEnum.name()));
|
|
|
|
reject(u"qmltypes misses name entry for enum"_s);
|
|
|
|
}
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString lookup = u"aotContext->getEnumLookup("_s + QString::number(index)
|
|
|
|
+ u", &"_s + m_state.accumulatorVariableOut + u')';
|
|
|
|
const QString initialization = u"aotContext->initGetEnumLookup("_s
|
|
|
|
+ QString::number(index) + u", "_s + metaObject(scopeType)
|
|
|
|
+ u", \""_s + enumName + u"\", \""_s + enumMember
|
|
|
|
+ u"\")"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
generateLookup(lookup, initialization);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generateTypeLookup(int index)
|
|
|
|
{
|
|
|
|
const QString indexString = QString::number(index);
|
2022-11-28 15:07:24 +00:00
|
|
|
const QQmlJSRegisterContent accumulatorIn = m_state.registers.value(Accumulator).content;
|
2021-11-16 15:49:49 +00:00
|
|
|
const QString namespaceString
|
2022-02-02 18:08:29 +00:00
|
|
|
= accumulatorIn.isImportNamespace()
|
|
|
|
? QString::number(accumulatorIn.importNamespace())
|
2022-03-21 09:21:18 +00:00
|
|
|
: u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-02-02 18:08:29 +00:00
|
|
|
switch (m_state.accumulatorOut().variant()) {
|
2021-11-16 15:49:49 +00:00
|
|
|
case QQmlJSRegisterContent::Singleton: {
|
2022-03-21 09:21:18 +00:00
|
|
|
rejectIfNonQObjectOut(u"non-QObject singleton type"_s);
|
|
|
|
const QString lookup = u"aotContext->loadSingletonLookup("_s + indexString
|
|
|
|
+ u", &"_s + m_state.accumulatorVariableOut + u')';
|
|
|
|
const QString initialization = u"aotContext->initLoadSingletonLookup("_s + indexString
|
|
|
|
+ u", "_s + namespaceString + u')';
|
2021-11-16 15:49:49 +00:00
|
|
|
generateLookup(lookup, initialization);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QQmlJSRegisterContent::ScopeModulePrefix:
|
|
|
|
break;
|
|
|
|
case QQmlJSRegisterContent::ScopeAttached: {
|
2022-03-21 09:21:18 +00:00
|
|
|
rejectIfNonQObjectOut(u"non-QObject attached type"_s);
|
|
|
|
const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
|
|
|
|
+ u", aotContext->qmlScopeObject, &"_s + m_state.accumulatorVariableOut + u')';
|
|
|
|
const QString initialization = u"aotContext->initLoadAttachedLookup("_s + indexString
|
|
|
|
+ u", "_s + namespaceString + u", aotContext->qmlScopeObject)"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
generateLookup(lookup, initialization);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QQmlJSRegisterContent::Script:
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"script lookup"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
break;
|
|
|
|
case QQmlJSRegisterContent::MetaType: {
|
2022-04-22 11:36:23 +00:00
|
|
|
if (!m_typeResolver->registerIsStoredIn(
|
|
|
|
m_state.accumulatorOut(), m_typeResolver->metaObjectType())) {
|
|
|
|
// TODO: Can we trigger this somehow?
|
|
|
|
// It might be impossible, but we better be safe here.
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"meta-object stored in different type"_s);
|
2022-04-22 11:36:23 +00:00
|
|
|
}
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString lookup = u"aotContext->loadTypeLookup("_s + indexString
|
|
|
|
+ u", &"_s + m_state.accumulatorVariableOut + u')';
|
|
|
|
const QString initialization = u"aotContext->initLoadTypeLookup("_s + indexString
|
|
|
|
+ u", "_s + namespaceString + u")"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
generateLookup(lookup, initialization);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-13 00:43:09 +00:00
|
|
|
void QQmlJSCodeGenerator::generateVariantEqualityComparison(
|
|
|
|
const QQmlJSRegisterContent &nonStorableContent, const QString ®isterName, bool invert)
|
|
|
|
{
|
|
|
|
const auto nonStorableType = m_typeResolver->containedType(nonStorableContent);
|
|
|
|
QQmlJSScope::ConstPtr comparedType =
|
|
|
|
m_typeResolver->equals(nonStorableType, m_typeResolver->nullType())
|
|
|
|
? m_typeResolver->nullType()
|
|
|
|
: m_typeResolver->voidType();
|
|
|
|
|
|
|
|
// The common operations for both nulltype and voidtype
|
|
|
|
m_body += u"if ("_s + registerName
|
|
|
|
+ u".metaType() == QMetaType::fromType<QJSPrimitiveValue>()) {\n"_s
|
|
|
|
+ m_state.accumulatorVariableOut + u" = "_s
|
2023-02-21 10:47:08 +00:00
|
|
|
+ conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
|
2022-12-13 00:43:09 +00:00
|
|
|
u"static_cast<const QJSPrimitiveValue *>("_s + registerName
|
|
|
|
+ u".constData())"_s + u"->type() "_s
|
|
|
|
+ (invert ? u"!="_s : u"=="_s)
|
|
|
|
+ (m_typeResolver->equals(comparedType, m_typeResolver->nullType())
|
|
|
|
? u"QJSPrimitiveValue::Null"_s
|
|
|
|
: u"QJSPrimitiveValue::Undefined"_s))
|
|
|
|
+ u";\n} else if ("_s + registerName
|
|
|
|
+ u".metaType() == QMetaType::fromType<QJSValue>()) {\n"_s
|
|
|
|
+ m_state.accumulatorVariableOut + u" = "_s
|
2023-02-21 10:47:08 +00:00
|
|
|
+ conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
|
2022-12-13 00:43:09 +00:00
|
|
|
(invert ? u"!"_s : QString()) + u"static_cast<const QJSValue *>("_s
|
|
|
|
+ registerName + u".constData())"_s + u"->"_s
|
|
|
|
+ (m_typeResolver->equals(comparedType, m_typeResolver->nullType())
|
|
|
|
? u"isNull()"_s
|
|
|
|
: u"isUndefined()"_s))
|
|
|
|
+ u";\n}"_s;
|
|
|
|
|
|
|
|
// Generate nullType specific operations (the case when variant contains QObject * or
|
|
|
|
// std::nullptr_t)
|
|
|
|
if (m_typeResolver->equals(nonStorableType, m_typeResolver->nullType())) {
|
|
|
|
m_body += u"else if ("_s + registerName
|
|
|
|
+ u".metaType().flags().testFlag(QMetaType::PointerToQObject)) {\n"_s
|
|
|
|
+ m_state.accumulatorVariableOut + u" = "_s
|
2023-02-21 10:47:08 +00:00
|
|
|
+ conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
|
2022-12-13 00:43:09 +00:00
|
|
|
u"*static_cast<QObject *const *>("_s + registerName
|
|
|
|
+ u".constData())"_s + (invert ? u"!="_s : u"=="_s)
|
|
|
|
+ u" nullptr"_s)
|
|
|
|
+ u";\n} else if ("_s + registerName
|
|
|
|
+ u".metaType() == QMetaType::fromType<std::nullptr_t>()) {\n"_s
|
2023-06-09 11:57:18 +00:00
|
|
|
+ m_state.accumulatorVariableOut + u" = "_s
|
|
|
|
+ conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
|
|
|
|
(invert ? u"false"_s : u"true"_s))
|
2022-12-13 00:43:09 +00:00
|
|
|
+ u";\n}\n"_s;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fallback case (if variant contains a different type, then it is not null or undefined)
|
|
|
|
m_body += u"else {\n"_s + m_state.accumulatorVariableOut + u" = "_s
|
2023-06-09 11:57:18 +00:00
|
|
|
+ conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
|
|
|
|
(invert ? (registerName + u".isValid() ? true : false"_s)
|
|
|
|
: (registerName + u".isValid() ? false : true"_s)))
|
|
|
|
+ u";\n}"_s;
|
2022-12-13 00:43:09 +00:00
|
|
|
}
|
|
|
|
|
2023-12-05 12:03:34 +00:00
|
|
|
void QQmlJSCodeGenerator::generateVariantEqualityComparison(
|
|
|
|
const QQmlJSRegisterContent &storableContent, const QString &typedRegisterName,
|
|
|
|
const QString &varRegisterName, bool invert)
|
|
|
|
{
|
|
|
|
// enumerations are ===-equal to their underlying type and they are stored as such.
|
|
|
|
// Therefore, use the underlying type right away.
|
|
|
|
const auto contained = storableContent.isEnumeration()
|
|
|
|
? storableContent.storedType()
|
|
|
|
: m_typeResolver->containedType(storableContent);
|
|
|
|
|
|
|
|
if (contained->isReferenceType()) {
|
|
|
|
const QQmlJSRegisterContent comparable
|
|
|
|
= m_typeResolver->builtinType(m_typeResolver->qObjectType());
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s + (invert ? u"!"_s : QString()) + u"(("
|
|
|
|
+ varRegisterName + u".metaType().flags() & QMetaType::PointerToQObject) "_s
|
|
|
|
+ u" && "_s + conversion(storableContent, comparable, typedRegisterName) + u" == "_s
|
|
|
|
+ conversion(m_typeResolver->varType(), comparable, varRegisterName) + u");\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_typeResolver->isPrimitive(contained)) {
|
|
|
|
const QQmlJSRegisterContent comparable
|
|
|
|
= m_typeResolver->builtinType(m_typeResolver->jsPrimitiveType());
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s + (invert ? u"!"_s : QString())
|
|
|
|
+ conversion(storableContent, comparable, typedRegisterName)
|
|
|
|
+ u".strictlyEquals("_s
|
|
|
|
+ conversion(m_typeResolver->varType(), comparable, varRegisterName) + u");\n"_s;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
reject(u"comparison of non-primitive, non-object type to var"_s);
|
|
|
|
}
|
|
|
|
|
2023-07-28 14:41:09 +00:00
|
|
|
void QQmlJSCodeGenerator::generateArrayInitializer(int argc, int argv)
|
|
|
|
{
|
|
|
|
const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
|
|
|
|
const QQmlJSScope::ConstPtr value = stored->valueType();
|
|
|
|
Q_ASSERT(value);
|
|
|
|
|
|
|
|
QStringList initializer;
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
|
|
|
initializer += convertStored(
|
|
|
|
registerType(argv + i).storedType(), value,
|
|
|
|
consumedRegisterVariable(argv + i));
|
|
|
|
}
|
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s + stored->internalName() + u'{';
|
|
|
|
m_body += initializer.join(u", "_s);
|
|
|
|
m_body += u"};\n";
|
|
|
|
}
|
|
|
|
|
2023-08-23 15:00:25 +00:00
|
|
|
void QQmlJSCodeGenerator::generateWriteBack(int registerIndex)
|
|
|
|
{
|
|
|
|
QString writeBackRegister = registerVariable(registerIndex);
|
|
|
|
bool writeBackAffectedBySideEffects = m_state.isRegisterAffectedBySideEffects(registerIndex);
|
|
|
|
|
|
|
|
for (QQmlJSRegisterContent writeBack = registerType(registerIndex);
|
|
|
|
!writeBack.storedType()->isReferenceType();) {
|
|
|
|
if (writeBackAffectedBySideEffects)
|
|
|
|
reject(u"write-back of value affected by side effects"_s);
|
|
|
|
|
|
|
|
if (writeBack.isConversion())
|
|
|
|
reject(u"write-back of converted value"_s);
|
|
|
|
|
|
|
|
const int lookupIndex = writeBack.resultLookupIndex();
|
|
|
|
if (lookupIndex == -1) {
|
2023-10-04 10:56:52 +00:00
|
|
|
// This is essential for the soundness of the type system.
|
|
|
|
//
|
|
|
|
// If a value or a list is returned from a function, we cannot know
|
|
|
|
// whether it is a copy or a reference. Therefore, we cannot know whether
|
|
|
|
// we have to write it back and so we have to reject any write on it.
|
|
|
|
//
|
|
|
|
// Only if we are sure that the value is locally created we can be sure
|
|
|
|
// we don't have to write it back. In this latter case we could allow
|
|
|
|
// a modification that doesn't write back.
|
2023-08-23 15:00:25 +00:00
|
|
|
reject(u"write-back of non-lookup"_s);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QString writeBackIndexString = QString::number(lookupIndex);
|
|
|
|
|
|
|
|
const QQmlJSRegisterContent::ContentVariant variant = writeBack.variant();
|
|
|
|
if (variant == QQmlJSRegisterContent::ScopeProperty
|
|
|
|
|| variant == QQmlJSRegisterContent::ExtensionScopeProperty) {
|
|
|
|
const QString lookup = u"aotContext->writeBackScopeObjectPropertyLookup("_s
|
|
|
|
+ writeBackIndexString
|
|
|
|
+ u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
|
|
|
|
const QString initialization
|
|
|
|
= u"aotContext->initLoadScopeObjectPropertyLookup("_s
|
|
|
|
+ writeBackIndexString
|
|
|
|
+ u", "_s + contentType(writeBack, writeBackRegister) + u')';
|
|
|
|
generateLookup(lookup, initialization);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QQmlJSRegisterContent outerContent;
|
|
|
|
QString outerRegister;
|
|
|
|
bool outerAffectedBySideEffects = false;
|
|
|
|
for (auto it = m_state.lookups.constBegin(), end = m_state.lookups.constEnd();
|
|
|
|
it != end; ++it) {
|
|
|
|
if (it.value().content.resultLookupIndex() == writeBack.baseLookupIndex()) {
|
|
|
|
outerContent = it.value().content;
|
|
|
|
outerRegister = lookupVariable(outerContent.resultLookupIndex());
|
|
|
|
outerAffectedBySideEffects = it.value().affectedBySideEffects;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!outerContent.isValid()) {
|
|
|
|
// If the lookup doesn't exist, it was killed by state merge.
|
|
|
|
reject(u"write-back of lookup across jumps or merges."_s);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_ASSERT(!outerRegister.isEmpty());
|
|
|
|
|
|
|
|
switch (writeBack.variant()) {
|
|
|
|
case QQmlJSRegisterContent::ScopeProperty:
|
|
|
|
case QQmlJSRegisterContent::ExtensionScopeProperty:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
case QQmlJSRegisterContent::ObjectProperty:
|
|
|
|
case QQmlJSRegisterContent::ExtensionObjectProperty:
|
|
|
|
if (writeBack.scopeType()->isReferenceType()) {
|
|
|
|
const QString lookup = u"aotContext->writeBackObjectLookup("_s
|
|
|
|
+ writeBackIndexString
|
|
|
|
+ u", "_s + outerRegister
|
|
|
|
+ u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
|
|
|
|
const QString initialization = u"aotContext->initGetObjectLookup("_s
|
|
|
|
+ writeBackIndexString
|
|
|
|
+ u", "_s + outerRegister
|
|
|
|
+ u", "_s + contentType(writeBack, writeBackRegister) + u')';
|
|
|
|
generateLookup(lookup, initialization);
|
|
|
|
} else {
|
|
|
|
const QString valuePointer = contentPointer(outerContent, outerRegister);
|
|
|
|
const QString lookup = u"aotContext->writeBackValueLookup("_s
|
|
|
|
+ writeBackIndexString
|
|
|
|
+ u", "_s + valuePointer
|
|
|
|
+ u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
|
|
|
|
const QString initialization = u"aotContext->initGetValueLookup("_s
|
|
|
|
+ writeBackIndexString
|
|
|
|
+ u", "_s + metaObject(writeBack.scopeType())
|
|
|
|
+ u", "_s + contentType(writeBack, writeBackRegister) + u')';
|
|
|
|
generateLookup(lookup, initialization);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
reject(u"SetLookup on value types (because of missing write-back)"_s);
|
|
|
|
}
|
|
|
|
|
|
|
|
writeBackRegister = outerRegister;
|
|
|
|
writeBack = outerContent;
|
|
|
|
writeBackAffectedBySideEffects = outerAffectedBySideEffects;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-22 11:36:23 +00:00
|
|
|
void QQmlJSCodeGenerator::rejectIfNonQObjectOut(const QString &error)
|
|
|
|
{
|
|
|
|
if (m_state.accumulatorOut().storedType()->accessSemantics()
|
|
|
|
!= QQmlJSScope::AccessSemantics::Reference) {
|
|
|
|
reject(error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-28 14:41:09 +00:00
|
|
|
void QQmlJSCodeGenerator::rejectIfBadArray()
|
|
|
|
{
|
|
|
|
const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
|
|
|
|
if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
|
|
|
|
// This rejects any attempt to store the list into a QVariant.
|
|
|
|
// Therefore, we don't have to adjust the contained type below.
|
|
|
|
|
|
|
|
reject(u"storing an array in a non-sequence type"_s);
|
|
|
|
} else if (stored->isListProperty()) {
|
|
|
|
// We can, technically, generate code for this. But it's dangerous:
|
|
|
|
//
|
|
|
|
// const QString storage = m_state.accumulatorVariableOut + u"_storage"_s;
|
|
|
|
// m_body += stored->internalName() + u"::ListType " + storage
|
|
|
|
// + u" = {"_s + initializer.join(u", "_s) + u"};\n"_s;
|
|
|
|
// m_body += m_state.accumulatorVariableOut
|
|
|
|
// + u" = " + stored->internalName() + u"(nullptr, &"_s + storage + u");\n"_s;
|
|
|
|
|
|
|
|
reject(u"creating a QQmlListProperty not backed by a property"_s);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-18 08:55:20 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
*
|
|
|
|
* generates a check for the content pointer to be valid.
|
|
|
|
* Returns true if the content pointer needs to be retrieved from a QVariant, or
|
|
|
|
* false if the variable can be used as-is.
|
|
|
|
*/
|
2023-02-16 09:54:34 +00:00
|
|
|
bool QQmlJSCodeGenerator::generateContentPointerCheck(
|
|
|
|
const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
|
|
|
|
const QString &variable, const QString &errorMessage)
|
|
|
|
{
|
|
|
|
const QQmlJSScope::ConstPtr scope = required;
|
|
|
|
const QQmlJSScope::ConstPtr input = m_typeResolver->containedType(actual);
|
|
|
|
if (QQmlJSUtils::searchBaseAndExtensionTypes(input,
|
|
|
|
[&](const QQmlJSScope::ConstPtr &base) {
|
|
|
|
return m_typeResolver->equals(base, scope);
|
|
|
|
})) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_typeResolver->canHold(input, scope)) {
|
|
|
|
reject(u"lookup of members of %1 in %2"_s.arg(
|
|
|
|
scope->internalName(), input->internalName()));
|
|
|
|
}
|
2023-04-18 08:55:20 +00:00
|
|
|
|
|
|
|
bool needsVarContentConversion = false;
|
|
|
|
QString processedErrorMessage;
|
|
|
|
if (actual.storedType()->isReferenceType()) {
|
|
|
|
// Since we have verified the type in qqmljstypepropagator.cpp we now know
|
|
|
|
// that we can only have either null or the actual type here. Therefore,
|
|
|
|
// it's enough to check the pointer for null.
|
|
|
|
m_body += u"if ("_s + variable + u" == nullptr) {\n "_s;
|
|
|
|
processedErrorMessage = errorMessage.arg(u"null");
|
|
|
|
} else if (m_typeResolver->equals(actual.storedType(), m_typeResolver->varType())) {
|
|
|
|
// Since we have verified the type in qqmljstypepropagator.cpp we now know
|
|
|
|
// that we can only have either undefined or the actual type here. Therefore,
|
|
|
|
// it's enough to check the QVariant for isValid().
|
|
|
|
m_body += u"if (!"_s + variable + u".isValid()) {\n "_s;
|
|
|
|
needsVarContentConversion = true;
|
|
|
|
processedErrorMessage = errorMessage.arg(u"undefined");
|
|
|
|
} else {
|
2023-02-16 09:54:34 +00:00
|
|
|
reject(u"retrieving metatype from %1"_s.arg(actual.descriptiveName()));
|
|
|
|
}
|
|
|
|
|
|
|
|
generateSetInstructionPointer();
|
|
|
|
m_body += u" aotContext->engine->throwError(QJSValue::TypeError, "_s;
|
2023-04-18 08:55:20 +00:00
|
|
|
m_body += u"QLatin1String(\"%1\"));\n"_s.arg(processedErrorMessage);
|
2023-02-16 09:54:34 +00:00
|
|
|
m_body += u" return "_s + errorReturnValue() + u";\n"_s;
|
|
|
|
m_body += u"}\n"_s;
|
2023-04-18 08:55:20 +00:00
|
|
|
return needsVarContentConversion;
|
2023-02-16 09:54:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QString QQmlJSCodeGenerator::resolveValueTypeContentPointer(
|
|
|
|
const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
|
|
|
|
const QString &variable, const QString &errorMessage)
|
|
|
|
{
|
|
|
|
if (generateContentPointerCheck(required, actual, variable, errorMessage))
|
|
|
|
return variable + u".data()"_s;
|
|
|
|
return contentPointer(actual, variable);
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QQmlJSCodeGenerator::resolveQObjectPointer(
|
|
|
|
const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
|
|
|
|
const QString &variable, const QString &errorMessage)
|
|
|
|
{
|
|
|
|
if (generateContentPointerCheck(required, actual, variable, errorMessage))
|
|
|
|
return u"*static_cast<QObject *const *>("_s + variable + u".constData())"_s;
|
|
|
|
return variable;
|
|
|
|
}
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
void QQmlJSCodeGenerator::generate_GetLookup(int index)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_GetLookup);
|
2023-11-16 10:02:14 +00:00
|
|
|
generate_GetLookupHelper(index);
|
|
|
|
}
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2023-11-16 10:02:14 +00:00
|
|
|
void QQmlJSCodeGenerator::generate_GetLookupHelper(int index)
|
|
|
|
{
|
2022-02-02 18:08:29 +00:00
|
|
|
if (m_state.accumulatorOut().isMethod()) {
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"lookup of function property."_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-02-16 13:01:52 +00:00
|
|
|
if (m_state.accumulatorOut().isImportNamespace()) {
|
|
|
|
Q_ASSERT(m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectModulePrefix);
|
|
|
|
// If we have an object module prefix, we need to pass through the original object.
|
|
|
|
if (m_state.accumulatorVariableIn != m_state.accumulatorVariableOut) {
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s
|
2022-02-16 13:01:52 +00:00
|
|
|
+ conversion(m_state.accumulatorIn(), m_state.accumulatorOut(),
|
2022-02-17 12:04:35 +00:00
|
|
|
m_state.accumulatorVariableIn)
|
2022-03-21 09:21:18 +00:00
|
|
|
+ u";\n"_s;
|
2022-02-16 13:01:52 +00:00
|
|
|
}
|
2021-11-16 15:49:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-02-16 13:01:52 +00:00
|
|
|
AccumulatorConverter registers(this);
|
|
|
|
|
|
|
|
if (m_state.accumulatorOut().isEnumeration()) {
|
|
|
|
generateEnumLookup(index);
|
|
|
|
return;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const QString indexString = QString::number(index);
|
2022-02-02 18:08:29 +00:00
|
|
|
const QString namespaceString = m_state.accumulatorIn().isImportNamespace()
|
|
|
|
? QString::number(m_state.accumulatorIn().importNamespace())
|
2022-03-21 09:21:18 +00:00
|
|
|
: u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
|
2022-02-04 21:35:23 +00:00
|
|
|
const auto accumulatorIn = m_state.accumulatorIn();
|
2023-02-16 09:54:34 +00:00
|
|
|
const QQmlJSScope::ConstPtr scope = m_state.accumulatorOut().scopeType();
|
|
|
|
const bool isReferenceType = scope->isReferenceType();
|
2022-02-16 12:33:47 +00:00
|
|
|
|
|
|
|
switch (m_state.accumulatorOut().variant()) {
|
|
|
|
case QQmlJSRegisterContent::ObjectAttached: {
|
2021-12-17 10:21:35 +00:00
|
|
|
if (!isReferenceType) {
|
|
|
|
// This can happen on incomplete type information. We contextually know that the
|
|
|
|
// type must be a QObject, but we cannot construct the inheritance chain. Then we
|
|
|
|
// store it in a generic type. Technically we could even convert it to QObject*, but
|
|
|
|
// that would be expensive.
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"attached object for non-QObject type"_s);
|
2021-12-17 10:21:35 +00:00
|
|
|
}
|
2023-04-03 08:15:07 +00:00
|
|
|
|
|
|
|
if (!m_state.accumulatorIn().storedType()->isReferenceType()) {
|
|
|
|
// This can happen if we retroactively determine that the property might not be
|
|
|
|
// what we think it is (ie, it can be shadowed).
|
|
|
|
reject(u"attached object of potentially non-QObject base"_s);
|
|
|
|
}
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
rejectIfNonQObjectOut(u"non-QObject attached type"_s);
|
2021-12-17 10:21:35 +00:00
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
|
|
|
|
+ u", "_s + m_state.accumulatorVariableIn
|
|
|
|
+ u", &"_s + m_state.accumulatorVariableOut + u')';
|
|
|
|
const QString initialization = u"aotContext->initLoadAttachedLookup("_s
|
|
|
|
+ indexString + u", "_s + namespaceString + u", "_s
|
2022-02-17 12:04:35 +00:00
|
|
|
+ m_state.accumulatorVariableIn + u')';
|
2021-11-16 15:49:49 +00:00
|
|
|
generateLookup(lookup, initialization);
|
|
|
|
return;
|
2022-02-16 12:33:47 +00:00
|
|
|
}
|
2022-04-22 11:36:23 +00:00
|
|
|
case QQmlJSRegisterContent::ScopeAttached:
|
|
|
|
case QQmlJSRegisterContent::Singleton:
|
2022-03-23 09:04:22 +00:00
|
|
|
case QQmlJSRegisterContent::MetaType: {
|
2022-04-22 11:36:23 +00:00
|
|
|
generateTypeLookup(index);
|
2022-03-23 09:04:22 +00:00
|
|
|
return;
|
|
|
|
}
|
2022-02-16 12:33:47 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-02-02 18:08:29 +00:00
|
|
|
Q_ASSERT(m_state.accumulatorOut().isProperty());
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2023-02-16 09:54:34 +00:00
|
|
|
if (m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->jsValueType())) {
|
|
|
|
reject(u"lookup in QJSValue"_s);
|
|
|
|
} else if (isReferenceType) {
|
|
|
|
const QString inputPointer = resolveQObjectPointer(
|
|
|
|
scope, accumulatorIn, m_state.accumulatorVariableIn,
|
2023-04-18 08:55:20 +00:00
|
|
|
u"Cannot read property '%1' of %2"_s.arg(
|
2023-02-16 09:54:34 +00:00
|
|
|
m_jsUnitGenerator->lookupName(index)));
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString lookup = u"aotContext->getObjectLookup("_s + indexString
|
2023-02-16 09:54:34 +00:00
|
|
|
+ u", "_s + inputPointer + u", "_s
|
2022-02-02 18:08:29 +00:00
|
|
|
+ contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString initialization = u"aotContext->initGetObjectLookup("_s
|
2023-02-16 09:54:34 +00:00
|
|
|
+ indexString + u", "_s + inputPointer
|
2022-03-21 09:21:18 +00:00
|
|
|
+ u", "_s + contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut)
|
2021-11-16 15:49:49 +00:00
|
|
|
+ u')';
|
|
|
|
const QString preparation = getLookupPreparation(
|
2022-02-02 18:08:29 +00:00
|
|
|
m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
|
2021-11-16 15:49:49 +00:00
|
|
|
generateLookup(lookup, initialization, preparation);
|
2023-07-24 19:15:25 +00:00
|
|
|
} else if ((scope->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
|
|
|
|
|| m_typeResolver->equals(scope, m_typeResolver->stringType()))
|
2022-03-21 09:21:18 +00:00
|
|
|
&& m_jsUnitGenerator->lookupName(index) == u"length"_s) {
|
2023-05-09 08:47:06 +00:00
|
|
|
const QQmlJSScope::ConstPtr stored = accumulatorIn.storedType();
|
|
|
|
if (stored->isListProperty()) {
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s;
|
|
|
|
m_body += conversion(
|
|
|
|
m_typeResolver->globalType(m_typeResolver->int32Type()),
|
|
|
|
m_state.accumulatorOut(),
|
|
|
|
m_state.accumulatorVariableIn + u".count("_s + u'&'
|
|
|
|
+ m_state.accumulatorVariableIn + u')');
|
|
|
|
m_body += u";\n"_s;
|
|
|
|
} else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
|
|
|
|
|| m_typeResolver->equals(stored, m_typeResolver->stringType())) {
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s
|
|
|
|
+ conversion(m_typeResolver->globalType(m_typeResolver->int32Type()),
|
|
|
|
m_state.accumulatorOut(),
|
|
|
|
m_state.accumulatorVariableIn + u".length()"_s)
|
|
|
|
+ u";\n"_s;
|
|
|
|
} else {
|
|
|
|
reject(u"access to 'length' property of sequence wrapped in non-sequence"_s);
|
|
|
|
}
|
2023-02-20 08:57:06 +00:00
|
|
|
} else if (m_typeResolver->registerIsStoredIn(accumulatorIn,
|
|
|
|
m_typeResolver->variantMapType())) {
|
|
|
|
QString mapLookup = m_state.accumulatorVariableIn + u"["_s
|
|
|
|
+ QQmlJSUtils::toLiteral(m_jsUnitGenerator->lookupName(index)) + u"]"_s;
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s;
|
|
|
|
m_body += conversion(m_typeResolver->globalType(m_typeResolver->varType()),
|
|
|
|
m_state.accumulatorOut(), mapLookup);
|
|
|
|
m_body += u";\n"_s;
|
2023-04-28 12:02:20 +00:00
|
|
|
} else {
|
|
|
|
if (m_state.isRegisterAffectedBySideEffects(Accumulator))
|
|
|
|
reject(u"reading from a value that's potentially affected by side effects"_s);
|
2023-02-16 09:54:34 +00:00
|
|
|
|
|
|
|
const QString inputContentPointer = resolveValueTypeContentPointer(
|
|
|
|
scope, accumulatorIn, m_state.accumulatorVariableIn,
|
2023-04-18 08:55:20 +00:00
|
|
|
u"Cannot read property '%1' of %2"_s.arg(
|
2023-02-16 09:54:34 +00:00
|
|
|
m_jsUnitGenerator->lookupName(index)));
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString lookup = u"aotContext->getValueLookup("_s + indexString
|
2023-02-16 09:54:34 +00:00
|
|
|
+ u", "_s + inputContentPointer
|
|
|
|
+ u", "_s + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut)
|
2021-11-16 15:49:49 +00:00
|
|
|
+ u')';
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString initialization = u"aotContext->initGetValueLookup("_s
|
|
|
|
+ indexString + u", "_s
|
2023-02-16 09:54:34 +00:00
|
|
|
+ metaObject(scope) + u", "_s
|
2022-02-02 18:08:29 +00:00
|
|
|
+ contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
|
2021-11-16 15:49:49 +00:00
|
|
|
const QString preparation = getLookupPreparation(
|
2022-02-02 18:08:29 +00:00
|
|
|
m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
|
2021-11-16 15:49:49 +00:00
|
|
|
generateLookup(lookup, initialization, preparation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_GetOptionalLookup(int index, int offset)
|
|
|
|
{
|
2023-11-16 10:02:14 +00:00
|
|
|
INJECT_TRACE_INFO(generate_GetOptionalLookup);
|
|
|
|
|
|
|
|
auto accumulatorIn = m_state.accumulatorIn();
|
|
|
|
QString accumulatorVarIn = m_state.accumulatorVariableIn;
|
|
|
|
|
|
|
|
const auto &annotation = (*m_annotations)[currentInstructionOffset()];
|
|
|
|
if (accumulatorIn.storedType()->isReferenceType()) {
|
|
|
|
m_body += u"if (!%1)\n"_s.arg(accumulatorVarIn);
|
|
|
|
generateJumpCodeWithTypeConversions(offset);
|
|
|
|
} else if (m_typeResolver->equals(accumulatorIn.storedType(), m_typeResolver->varType())) {
|
|
|
|
m_body += u"if (!%1.isValid() || ((%1.metaType().flags() & QMetaType::PointerToQObject) "
|
|
|
|
"&& %1.value<QObject *>() == nullptr))\n"_s.arg(accumulatorVarIn);
|
|
|
|
generateJumpCodeWithTypeConversions(offset);
|
|
|
|
} else if (m_typeResolver->equals(accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType())) {
|
|
|
|
m_body += u"if (%1.equals(QJSPrimitiveUndefined()) "
|
|
|
|
"|| %1.equals(QJSPrimitiveNull()))\n"_s.arg(accumulatorVarIn);
|
|
|
|
generateJumpCodeWithTypeConversions(offset);
|
|
|
|
} else if (annotation.changedRegisterIndex == Accumulator
|
|
|
|
&& annotation.changedRegister.variant() == QQmlJSRegisterContent::ObjectEnum) {
|
|
|
|
// Nothing
|
2024-01-22 13:44:27 +00:00
|
|
|
} else if (m_typeResolver->equals(accumulatorIn.storedType(), m_typeResolver->jsValueType())) {
|
|
|
|
m_body += u"if (%1.isNull() || %1.isUndefined())\n"_s.arg(accumulatorVarIn);
|
|
|
|
generateJumpCodeWithTypeConversions(offset);
|
2023-11-16 10:02:14 +00:00
|
|
|
} else {
|
|
|
|
Q_UNREACHABLE(); // No other accumulatorIn stored types should be possible
|
|
|
|
}
|
|
|
|
|
|
|
|
generate_GetLookupHelper(index);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_StoreProperty(int nameIndex, int baseReg)
|
|
|
|
{
|
|
|
|
Q_UNUSED(nameIndex)
|
|
|
|
Q_UNUSED(baseReg)
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"StoreProperty"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QString QQmlJSCodeGenerator::setLookupPreparation(
|
|
|
|
const QQmlJSRegisterContent &content, const QString &arg, int lookup)
|
|
|
|
{
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->registerContains(content, content.storedType()))
|
2021-11-16 15:49:49 +00:00
|
|
|
return QString();
|
2022-02-04 21:35:23 +00:00
|
|
|
|
|
|
|
if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())) {
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"const QMetaType argType = aotContext->lookupResultMetaType("_s
|
|
|
|
+ QString::number(lookup) + u");\n"_s
|
|
|
|
+ u"if (argType.isValid())\n "_s + arg + u".convert(argType)";
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
// TODO: We could make sure they're compatible, for example QObject pointers.
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_SetLookup);
|
|
|
|
|
|
|
|
const QString indexString = QString::number(index);
|
2022-02-02 18:08:29 +00:00
|
|
|
const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn().storedType();
|
2023-04-18 08:55:20 +00:00
|
|
|
const QQmlJSRegisterContent callBase = m_typeResolver->original(registerType(baseReg));
|
2023-07-27 09:42:39 +00:00
|
|
|
const QQmlJSRegisterContent specific = m_state.readAccumulator();
|
|
|
|
Q_ASSERT(specific.isConversion());
|
|
|
|
const QQmlJSScope::ConstPtr conversionResultScope = specific.conversionResultScope();
|
2023-04-18 08:55:20 +00:00
|
|
|
|
2023-05-15 07:58:15 +00:00
|
|
|
if (specific.storedType().isNull()) {
|
|
|
|
reject(u"SetLookup. Could not find property "
|
|
|
|
+ m_jsUnitGenerator->lookupName(index)
|
|
|
|
+ u" on type "
|
|
|
|
+ callBase.storedType()->internalName());
|
|
|
|
return;
|
|
|
|
}
|
2023-03-14 13:53:26 +00:00
|
|
|
|
|
|
|
// Choose a container that can hold both, the "in" accumulator and what we actually want.
|
|
|
|
// If the types are all the same because we can all store them as verbatim C++ types,
|
|
|
|
// the container will also be that type.
|
2023-12-08 12:55:24 +00:00
|
|
|
|
|
|
|
QQmlJSRegisterContent property = specific;
|
|
|
|
if (!m_typeResolver->equals(specific.storedType(), valueType)) {
|
|
|
|
if (m_typeResolver->isPrimitive(specific.storedType())
|
|
|
|
&& m_typeResolver->isPrimitive(valueType)) {
|
|
|
|
// Preferably store in QJSPrimitiveValue since we need the content pointer below.
|
|
|
|
property = property.storedIn(m_typeResolver->jsPrimitiveType());
|
|
|
|
} else {
|
|
|
|
property = property.storedIn(m_typeResolver->merge(specific.storedType(), valueType));
|
|
|
|
}
|
|
|
|
}
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-02-17 12:04:35 +00:00
|
|
|
const QString object = registerVariable(baseReg);
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"{\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
QString variableIn;
|
|
|
|
QString variableInType;
|
|
|
|
QString preparation;
|
|
|
|
QString argType;
|
2022-02-04 21:35:23 +00:00
|
|
|
if (!m_typeResolver->registerContains(
|
|
|
|
m_state.accumulatorIn(), m_typeResolver->containedType(property))) {
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"auto converted = "_s
|
2022-11-28 15:07:24 +00:00
|
|
|
+ conversion(m_state.accumulatorIn(), property, consumedAccumulatorVariableIn())
|
2022-03-21 09:21:18 +00:00
|
|
|
+ u";\n"_s;
|
|
|
|
variableIn = contentPointer(property, u"converted"_s);
|
|
|
|
variableInType = contentType(property, u"converted"_s);
|
|
|
|
preparation = setLookupPreparation(property, u"converted"_s, index);
|
2021-11-16 15:49:49 +00:00
|
|
|
if (preparation.isEmpty())
|
2022-03-21 09:21:18 +00:00
|
|
|
argType = contentType(property, u"converted"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
else
|
2022-03-21 09:21:18 +00:00
|
|
|
argType = u"argType"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
} else {
|
2022-02-17 12:04:35 +00:00
|
|
|
variableIn = contentPointer(property, m_state.accumulatorVariableIn);
|
|
|
|
variableInType = contentType(property, m_state.accumulatorVariableIn);
|
2021-11-16 15:49:49 +00:00
|
|
|
argType = variableInType;
|
|
|
|
}
|
|
|
|
|
2023-07-27 09:42:39 +00:00
|
|
|
switch (conversionResultScope->accessSemantics()) {
|
2021-11-16 15:49:49 +00:00
|
|
|
case QQmlJSScope::AccessSemantics::Reference: {
|
2023-02-16 09:54:34 +00:00
|
|
|
const QString basePointer = resolveQObjectPointer(
|
2023-07-27 09:42:39 +00:00
|
|
|
conversionResultScope, registerType(baseReg), object,
|
2023-04-18 08:55:20 +00:00
|
|
|
u"TypeError: Value is %1 and could not be converted to an object"_s);
|
2023-02-16 09:54:34 +00:00
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString lookup = u"aotContext->setObjectLookup("_s + indexString
|
2023-02-16 09:54:34 +00:00
|
|
|
+ u", "_s + basePointer + u", "_s + variableIn + u')';
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString initialization = u"aotContext->initSetObjectLookup("_s
|
2023-02-16 09:54:34 +00:00
|
|
|
+ indexString + u", "_s + basePointer + u", "_s + argType + u')';
|
2021-11-16 15:49:49 +00:00
|
|
|
generateLookup(lookup, initialization, preparation);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QQmlJSScope::AccessSemantics::Sequence: {
|
|
|
|
const QString propertyName = m_jsUnitGenerator->lookupName(index);
|
2022-03-21 09:21:18 +00:00
|
|
|
if (propertyName != u"length"_s) {
|
|
|
|
reject(u"setting non-length property on a sequence type"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-07-27 09:42:39 +00:00
|
|
|
if (!conversionResultScope->isListProperty()) {
|
2023-04-28 12:02:20 +00:00
|
|
|
reject(u"resizing sequence types (because of missing write-back)"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We can resize without write back on a list property because it's actually a reference.
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"const int begin = "_s + object + u".count(&" + object + u");\n"_s;
|
|
|
|
m_body += u"const int end = "_s
|
2021-11-16 15:49:49 +00:00
|
|
|
+ (variableIn.startsWith(u'&') ? variableIn.mid(1) : (u'*' + variableIn))
|
2022-03-21 09:21:18 +00:00
|
|
|
+ u";\n"_s;
|
|
|
|
m_body += u"for (int i = begin; i < end; ++i)\n"_s;
|
|
|
|
m_body += u" "_s + object + u".append(&"_s + object + u", nullptr);\n"_s;
|
|
|
|
m_body += u"for (int i = begin; i > end; --i)\n"_s;
|
|
|
|
m_body += u" "_s + object + u".removeLast(&"_s + object + u')'
|
|
|
|
+ u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QQmlJSScope::AccessSemantics::Value: {
|
2023-08-14 14:03:05 +00:00
|
|
|
const QQmlJSRegisterContent base = registerType(baseReg);
|
2023-02-16 09:54:34 +00:00
|
|
|
const QString baseContentPointer = resolveValueTypeContentPointer(
|
2023-08-14 14:03:05 +00:00
|
|
|
conversionResultScope, base, object,
|
2023-04-18 08:55:20 +00:00
|
|
|
u"TypeError: Value is %1 and could not be converted to an object"_s);
|
2023-02-16 09:54:34 +00:00
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString lookup = u"aotContext->setValueLookup("_s + indexString
|
2023-02-16 09:54:34 +00:00
|
|
|
+ u", "_s + baseContentPointer
|
2022-03-21 09:21:18 +00:00
|
|
|
+ u", "_s + variableIn + u')';
|
|
|
|
const QString initialization = u"aotContext->initSetValueLookup("_s
|
2023-07-27 09:42:39 +00:00
|
|
|
+ indexString + u", "_s + metaObject(conversionResultScope)
|
2023-02-16 09:54:34 +00:00
|
|
|
+ u", "_s + argType + u')';
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
generateLookup(lookup, initialization, preparation);
|
2023-08-23 15:00:25 +00:00
|
|
|
generateWriteBack(baseReg);
|
2023-08-14 14:03:05 +00:00
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QQmlJSScope::AccessSemantics::None:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"}\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadSuperProperty(int property)
|
|
|
|
{
|
|
|
|
Q_UNUSED(property)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_StoreSuperProperty(int property)
|
|
|
|
{
|
|
|
|
Q_UNUSED(property)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_Yield()
|
|
|
|
{
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_YieldStar()
|
|
|
|
{
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_Resume(int)
|
|
|
|
{
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
|
|
|
|
{
|
|
|
|
QString types;
|
|
|
|
QString args;
|
|
|
|
|
2022-02-16 13:01:52 +00:00
|
|
|
if (m_state.changedRegisterIndex() == InvalidRegister ||
|
|
|
|
m_typeResolver->registerContains(
|
|
|
|
m_state.accumulatorOut(), m_typeResolver->voidType())) {
|
2022-03-21 09:21:18 +00:00
|
|
|
types = u"QMetaType()"_s;
|
|
|
|
args = u"nullptr"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
} else {
|
2022-03-21 09:21:18 +00:00
|
|
|
*outVar = u"callResult"_s;
|
2022-02-02 18:08:29 +00:00
|
|
|
const QQmlJSScope::ConstPtr outType = m_state.accumulatorOut().storedType();
|
2023-05-09 10:00:03 +00:00
|
|
|
m_body += outType->augmentedInternalName() + u' ' + *outVar;
|
|
|
|
if (!m_typeResolver->registerContains(m_state.accumulatorOut(), outType)) {
|
|
|
|
if (m_typeResolver->equals(outType, m_typeResolver->varType())
|
|
|
|
|| m_typeResolver->equals(outType, m_typeResolver->jsPrimitiveType())) {
|
|
|
|
m_body += u'('
|
|
|
|
+ metaType(m_typeResolver->containedType(m_state.accumulatorOut()))
|
|
|
|
+ u')';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_body += u";\n";
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2023-05-09 10:00:03 +00:00
|
|
|
args = contentPointer(m_state.accumulatorOut(), *outVar);
|
|
|
|
types = contentType(m_state.accumulatorOut(), *outVar);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
2022-02-04 21:35:23 +00:00
|
|
|
const QQmlJSRegisterContent content = registerType(argv + i);
|
2023-05-09 10:00:03 +00:00
|
|
|
const QString var = registerVariable(argv + i);
|
|
|
|
args += u", "_s + contentPointer(content, var);
|
|
|
|
types += u", "_s + contentType(content, var);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
2023-05-09 10:00:03 +00:00
|
|
|
|
|
|
|
return u"void *args[] = { "_s + args + u" };\n"_s
|
2022-03-21 09:21:18 +00:00
|
|
|
+ u"const QMetaType types[] = { "_s + types + u" };\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generateMoveOutVar(const QString &outVar)
|
|
|
|
{
|
2022-02-16 12:33:47 +00:00
|
|
|
if (m_state.accumulatorVariableOut.isEmpty() || outVar.isEmpty())
|
|
|
|
return;
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s;
|
2022-02-16 12:33:47 +00:00
|
|
|
m_body += u"std::move(" + outVar + u");\n";
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CallValue(int name, int argc, int argv)
|
|
|
|
{
|
|
|
|
Q_UNUSED(name)
|
|
|
|
Q_UNUSED(argc)
|
|
|
|
Q_UNUSED(argv)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CallWithReceiver(int name, int thisObject, int argc, int argv)
|
|
|
|
{
|
|
|
|
Q_UNUSED(name)
|
|
|
|
Q_UNUSED(thisObject)
|
|
|
|
Q_UNUSED(argc)
|
|
|
|
Q_UNUSED(argv)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CallProperty(int nameIndex, int baseReg, int argc, int argv)
|
|
|
|
{
|
|
|
|
Q_UNUSED(nameIndex);
|
|
|
|
Q_UNUSED(baseReg);
|
|
|
|
Q_UNUSED(argc);
|
|
|
|
Q_UNUSED(argv);
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"CallProperty"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2022-06-03 08:51:26 +00:00
|
|
|
bool QQmlJSCodeGenerator::inlineStringMethod(const QString &name, int base, int argc, int argv)
|
|
|
|
{
|
|
|
|
if (name != u"arg"_s || argc != 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const auto arg = [&](const QQmlJSScope::ConstPtr &type) {
|
2023-02-21 10:47:08 +00:00
|
|
|
return convertStored(registerType(argv).storedType(), type, consumedRegisterVariable(argv));
|
2022-06-03 08:51:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const auto ret = [&](const QString &arg) {
|
2023-02-21 10:47:08 +00:00
|
|
|
const QString expression = convertStored(
|
2022-06-03 08:51:26 +00:00
|
|
|
registerType(base).storedType(), m_typeResolver->stringType(),
|
2022-11-28 15:07:24 +00:00
|
|
|
consumedRegisterVariable(base)) + u".arg("_s + arg + u')';
|
2022-06-03 08:51:26 +00:00
|
|
|
return conversion(
|
2023-02-21 10:47:08 +00:00
|
|
|
m_typeResolver->stringType(), m_state.accumulatorOut(), expression);
|
2022-06-03 08:51:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const QQmlJSRegisterContent input = m_state.readRegister(argv);
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s;
|
|
|
|
|
2023-03-27 16:00:46 +00:00
|
|
|
if (m_typeResolver->isNumeric(input))
|
|
|
|
m_body += ret(arg(m_typeResolver->containedType(input)));
|
2022-06-03 08:51:26 +00:00
|
|
|
else if (m_typeResolver->registerContains(input, m_typeResolver->boolType()))
|
|
|
|
m_body += ret(arg(m_typeResolver->boolType()));
|
|
|
|
else
|
|
|
|
m_body += ret(arg(m_typeResolver->stringType()));
|
|
|
|
m_body += u";\n"_s;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-05-25 13:46:13 +00:00
|
|
|
bool QQmlJSCodeGenerator::inlineTranslateMethod(const QString &name, int argc, int argv)
|
|
|
|
{
|
2023-11-03 08:45:46 +00:00
|
|
|
addInclude(u"QtCore/qcoreapplication.h"_s);
|
2022-05-25 13:46:13 +00:00
|
|
|
|
|
|
|
const auto arg = [&](int i, const QQmlJSScope::ConstPtr &type) {
|
|
|
|
Q_ASSERT(i < argc);
|
2023-02-21 10:47:08 +00:00
|
|
|
return convertStored(registerType(argv + i).storedType(), type,
|
|
|
|
consumedRegisterVariable(argv + i));
|
2022-05-25 13:46:13 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const auto stringArg = [&](int i) {
|
|
|
|
return i < argc
|
|
|
|
? (arg(i, m_typeResolver->stringType()) + u".toUtf8().constData()"_s)
|
|
|
|
: u"\"\""_s;
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto intArg = [&](int i) {
|
2023-03-27 16:00:46 +00:00
|
|
|
return i < argc ? arg(i, m_typeResolver->int32Type()) : u"-1"_s;
|
2022-05-25 13:46:13 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const auto stringRet = [&](const QString &expression) {
|
|
|
|
return conversion(
|
2023-02-21 10:47:08 +00:00
|
|
|
m_typeResolver->stringType(), m_state.accumulatorOut(), expression);
|
2022-05-25 13:46:13 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const auto capture = [&]() {
|
|
|
|
m_body += u"aotContext->captureTranslation();\n"_s;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (name == u"QT_TRID_NOOP"_s || name == u"QT_TR_NOOP"_s) {
|
|
|
|
Q_ASSERT(argc > 0);
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s
|
|
|
|
+ stringRet(arg(0, m_typeResolver->stringType())) + u";\n"_s;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name == u"QT_TRANSLATE_NOOP"_s) {
|
|
|
|
Q_ASSERT(argc > 1);
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s
|
|
|
|
+ stringRet(arg(1, m_typeResolver->stringType())) + u";\n"_s;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name == u"qsTrId"_s) {
|
|
|
|
capture();
|
|
|
|
// We inline qtTrId() here because in the !QT_CONFIG(translation) case it's unavailable.
|
|
|
|
// QCoreApplication::translate() is always available in some primitive form.
|
|
|
|
// Also, this saves a function call.
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s
|
|
|
|
+ stringRet(u"QCoreApplication::translate(nullptr, "_s + stringArg(0) +
|
|
|
|
u", nullptr, "_s + intArg(1) + u")"_s) + u";\n"_s;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name == u"qsTr"_s) {
|
|
|
|
capture();
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s
|
|
|
|
+ stringRet(u"QCoreApplication::translate("_s
|
|
|
|
+ u"aotContext->translationContext().toUtf8().constData(), "_s
|
|
|
|
+ stringArg(0) + u", "_s + stringArg(1) + u", "_s
|
|
|
|
+ intArg(2) + u")"_s) + u";\n"_s;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name == u"qsTranslate"_s) {
|
|
|
|
capture();
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s
|
|
|
|
+ stringRet(u"QCoreApplication::translate("_s
|
|
|
|
+ stringArg(0) + u", "_s + stringArg(1) + u", "_s
|
|
|
|
+ stringArg(2) + u", "_s + intArg(3) + u")"_s) + u";\n"_s;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-02-14 15:18:06 +00:00
|
|
|
static QString maxExpression(int argc)
|
|
|
|
{
|
|
|
|
Q_ASSERT_X(argc >= 2, Q_FUNC_INFO, "max() expects at least two arguments.");
|
|
|
|
|
|
|
|
QString expression =
|
|
|
|
u"[&]() { \nauto tmpMax = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) ? arg2 : ((arg2 > arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
|
|
|
|
for (int i = 2; i < argc; i++) {
|
|
|
|
expression +=
|
|
|
|
"\ttmpMax = (qIsNull(%1) && qIsNull(tmpMax) && std::copysign(1.0, %1) == 1) ? arg2 : ((%1 > tmpMax || std::isnan(%1)) ? %1 : tmpMax);\n"_L1
|
|
|
|
.arg("arg"_L1 + QString::number(i + 1));
|
|
|
|
}
|
|
|
|
expression += "return tmpMax;\n}()"_L1;
|
|
|
|
|
|
|
|
return expression;
|
|
|
|
}
|
|
|
|
|
|
|
|
static QString minExpression(int argc)
|
|
|
|
{
|
|
|
|
Q_ASSERT_X(argc >= 2, Q_FUNC_INFO, "min() expects at least two arguments.");
|
|
|
|
|
|
|
|
QString expression =
|
|
|
|
u"[&]() { \nauto tmpMin = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) ? arg2 : ((arg2 < arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
|
|
|
|
for (int i = 2; i < argc; i++) {
|
|
|
|
expression +=
|
|
|
|
"tmpMin = (qIsNull(%1) && qIsNull(tmpMin) && std::copysign(1.0, %1) == -1) ? arg2 : ((%1 < tmpMin || std::isnan(%1)) ? %1 : tmpMin);\n"_L1
|
|
|
|
.arg("arg"_L1 + QString::number(i + 1));
|
|
|
|
}
|
|
|
|
expression += "return tmpMin;\n}()"_L1;
|
|
|
|
|
|
|
|
return expression;
|
|
|
|
}
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int argv)
|
|
|
|
{
|
2022-03-21 09:21:18 +00:00
|
|
|
addInclude(u"cmath"_s);
|
|
|
|
addInclude(u"limits"_s);
|
2023-11-03 08:45:46 +00:00
|
|
|
addInclude(u"QtCore/qalgorithms.h"_s);
|
|
|
|
addInclude(u"QtCore/qrandom.h"_s);
|
|
|
|
addInclude(u"QtQml/qjsprimitivevalue.h"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-02-16 13:01:52 +00:00
|
|
|
// If the result is not stored, we don't need to generate any code. All the math methods are
|
|
|
|
// conceptually pure functions.
|
|
|
|
if (m_state.changedRegisterIndex() != Accumulator)
|
|
|
|
return true;
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"{\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
for (int i = 0; i < argc; ++i) {
|
2023-02-21 10:47:08 +00:00
|
|
|
m_body += u"const double arg%1 = "_s.arg(i + 1) + convertStored(
|
2021-11-16 15:49:49 +00:00
|
|
|
registerType(argv + i).storedType(),
|
2022-11-28 15:07:24 +00:00
|
|
|
m_typeResolver->realType(), consumedRegisterVariable(argv + i))
|
2022-03-21 09:21:18 +00:00
|
|
|
+ u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString qNaN = u"std::numeric_limits<double>::quiet_NaN()"_s;
|
|
|
|
const QString inf = u"std::numeric_limits<double>::infinity()"_s;
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-02-16 13:01:52 +00:00
|
|
|
QString expression;
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
if (name == u"abs" && argc == 1) {
|
2022-03-21 09:21:18 +00:00
|
|
|
expression = u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_s;
|
|
|
|
} else if (name == u"acos"_s && argc == 1) {
|
|
|
|
expression = u"arg1 > 1.0 ? %1 : std::acos(arg1)"_s.arg(qNaN);
|
|
|
|
} else if (name == u"acosh"_s && argc == 1) {
|
|
|
|
expression = u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_s.arg(qNaN);
|
|
|
|
} else if (name == u"asin"_s && argc == 1) {
|
|
|
|
expression = u"arg1 > 1.0 ? %1 : std::asin(arg1)"_s.arg(qNaN);
|
|
|
|
} else if (name == u"asinh"_s && argc == 1) {
|
|
|
|
expression = u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_s;
|
|
|
|
} else if (name == u"atan"_s && argc == 1) {
|
|
|
|
expression = u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_s;
|
|
|
|
} else if (name == u"atanh"_s && argc == 1) {
|
|
|
|
expression = u"qIsNull(arg1) ? arg1 : std::atanh(arg1)"_s;
|
|
|
|
} else if (name == u"atan2"_s) {
|
2021-11-16 15:49:49 +00:00
|
|
|
// TODO: complicated
|
|
|
|
return false;
|
2022-03-21 09:21:18 +00:00
|
|
|
} else if (name == u"cbrt"_s && argc == 1) {
|
|
|
|
expression = u"std::cbrt(arg1)"_s;
|
|
|
|
} else if (name == u"ceil"_s && argc == 1) {
|
|
|
|
expression = u"(arg1 < 0.0 && arg1 > -1.0) ? std::copysign(0.0, -1.0) : std::ceil(arg1)"_s;
|
|
|
|
} else if (name == u"clz32"_s && argc == 1) {
|
|
|
|
expression = u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_s;
|
|
|
|
} else if (name == u"cos"_s && argc == 1) {
|
|
|
|
expression = u"std::cos(arg1)"_s;
|
|
|
|
} else if (name == u"cosh"_s && argc == 1) {
|
|
|
|
expression = u"std::cosh(arg1)"_s;
|
|
|
|
} else if (name == u"exp"_s && argc == 1) {
|
2022-02-16 13:01:52 +00:00
|
|
|
expression = u"std::isinf(arg1) "
|
2021-11-16 15:49:49 +00:00
|
|
|
"? (std::copysign(1.0, arg1) == -1 ? 0.0 : %1) "
|
2022-03-21 09:21:18 +00:00
|
|
|
": std::exp(arg1)"_s.arg(inf);
|
|
|
|
} else if (name == u"expm1"_s) {
|
2021-11-16 15:49:49 +00:00
|
|
|
// TODO: complicated
|
|
|
|
return false;
|
2022-03-21 09:21:18 +00:00
|
|
|
} else if (name == u"floor"_s && argc == 1) {
|
|
|
|
expression = u"std::floor(arg1)"_s;
|
|
|
|
} else if (name == u"fround"_s && argc == 1) {
|
2022-02-16 13:01:52 +00:00
|
|
|
expression = u"(std::isnan(arg1) || std::isinf(arg1) || qIsNull(arg1)) "
|
2021-11-16 15:49:49 +00:00
|
|
|
"? arg1 "
|
2022-03-21 09:21:18 +00:00
|
|
|
": double(float(arg1))"_s;
|
|
|
|
} else if (name == u"hypot"_s) {
|
2021-11-16 15:49:49 +00:00
|
|
|
// TODO: complicated
|
|
|
|
return false;
|
2022-03-21 09:21:18 +00:00
|
|
|
} else if (name == u"imul"_s && argc == 2) {
|
2022-02-16 13:01:52 +00:00
|
|
|
expression = u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) "
|
2022-03-21 09:21:18 +00:00
|
|
|
"* quint32(QJSNumberCoercion::toInteger(arg2)))"_s;
|
|
|
|
} else if (name == u"log"_s && argc == 1) {
|
|
|
|
expression = u"arg1 < 0.0 ? %1 : std::log(arg1)"_s.arg(qNaN);
|
|
|
|
} else if (name == u"log10"_s && argc == 1) {
|
|
|
|
expression = u"arg1 < 0.0 ? %1 : std::log10(arg1)"_s.arg(qNaN);
|
|
|
|
} else if (name == u"log1p"_s && argc == 1) {
|
|
|
|
expression = u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_s.arg(qNaN);
|
|
|
|
} else if (name == u"log2"_s && argc == 1) {
|
|
|
|
expression = u"arg1 < -0.0 ? %1 : std::log2(arg1)"_s.arg(qNaN);
|
2023-02-14 15:18:06 +00:00
|
|
|
} else if (name == u"max"_s && argc >= 2) {
|
|
|
|
expression = maxExpression(argc);
|
|
|
|
} else if (name == u"min"_s && argc >= 2) {
|
|
|
|
expression = minExpression(argc);
|
2022-03-21 09:21:18 +00:00
|
|
|
} else if (name == u"pow"_s) {
|
2022-07-19 12:00:28 +00:00
|
|
|
expression = u"QQmlPrivate::jsExponentiate(arg1, arg2)"_s;
|
2022-03-21 09:21:18 +00:00
|
|
|
} else if (name == u"random"_s && argc == 0) {
|
|
|
|
expression = u"QRandomGenerator::global()->generateDouble()"_s;
|
|
|
|
} else if (name == u"round"_s && argc == 1) {
|
2022-02-16 13:01:52 +00:00
|
|
|
expression = u"std::isfinite(arg1) "
|
2021-11-16 15:49:49 +00:00
|
|
|
"? ((arg1 < 0.5 && arg1 >= -0.5) "
|
|
|
|
"? std::copysign(0.0, arg1) "
|
|
|
|
": std::floor(arg1 + 0.5)) "
|
2022-03-21 09:21:18 +00:00
|
|
|
": arg1"_s;
|
|
|
|
} else if (name == u"sign"_s && argc == 1) {
|
2022-02-16 13:01:52 +00:00
|
|
|
expression = u"std::isnan(arg1) "
|
2021-11-16 15:49:49 +00:00
|
|
|
"? %1 "
|
|
|
|
": (qIsNull(arg1) "
|
|
|
|
"? arg1 "
|
2022-03-21 09:21:18 +00:00
|
|
|
": (std::signbit(arg1) ? -1.0 : 1.0))"_s.arg(qNaN);
|
|
|
|
} else if (name == u"sin"_s && argc == 1) {
|
|
|
|
expression = u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_s;
|
|
|
|
} else if (name == u"sinh"_s && argc == 1) {
|
|
|
|
expression = u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_s;
|
|
|
|
} else if (name == u"sqrt"_s && argc == 1) {
|
|
|
|
expression = u"std::sqrt(arg1)"_s;
|
|
|
|
} else if (name == u"tan"_s && argc == 1) {
|
|
|
|
expression = u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_s;
|
|
|
|
} else if (name == u"tanh"_s && argc == 1) {
|
|
|
|
expression = u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_s;
|
|
|
|
} else if (name == u"trunc"_s && argc == 1) {
|
|
|
|
expression = u"std::trunc(arg1)"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-02-21 10:47:08 +00:00
|
|
|
m_body += conversion(m_typeResolver->realType(), m_state.accumulatorOut(), expression);
|
2022-02-16 13:01:52 +00:00
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
|
|
|
m_body += u"}\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-11-15 09:18:37 +00:00
|
|
|
static QString messageTypeForMethod(const QString &method)
|
|
|
|
{
|
|
|
|
if (method == u"log" || method == u"debug")
|
|
|
|
return u"QtDebugMsg"_s;
|
|
|
|
if (method == u"info")
|
|
|
|
return u"QtInfoMsg"_s;
|
|
|
|
if (method == u"warn")
|
|
|
|
return u"QtWarningMsg"_s;
|
|
|
|
if (method == u"error")
|
|
|
|
return u"QtCriticalMsg"_s;
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QQmlJSCodeGenerator::inlineConsoleMethod(const QString &name, int argc, int argv)
|
|
|
|
{
|
|
|
|
const QString type = messageTypeForMethod(name);
|
|
|
|
if (type.isEmpty())
|
|
|
|
return false;
|
|
|
|
|
2023-11-03 08:45:46 +00:00
|
|
|
addInclude(u"QtCore/qloggingcategory.h"_s);
|
2022-11-15 09:18:37 +00:00
|
|
|
|
|
|
|
m_body += u"{\n";
|
|
|
|
m_body += u" bool firstArgIsCategory = false;\n";
|
|
|
|
const QQmlJSRegisterContent firstArg = argc > 0 ? registerType(argv) : QQmlJSRegisterContent();
|
|
|
|
|
|
|
|
// We could check for internalName == "QQmlLoggingCategory" here, but we don't want to
|
|
|
|
// because QQmlLoggingCategory is not a builtin. Tying the specific internal name and
|
|
|
|
// intheritance hierarchy in here would be fragile.
|
|
|
|
// TODO: We could drop the check for firstArg in some cases if we made some base class
|
|
|
|
// of QQmlLoggingCategory a builtin.
|
|
|
|
const bool firstArgIsReference = argc > 0
|
|
|
|
&& m_typeResolver->containedType(firstArg)->isReferenceType();
|
|
|
|
|
|
|
|
if (firstArgIsReference) {
|
|
|
|
m_body += u" QObject *firstArg = ";
|
2023-02-21 10:47:08 +00:00
|
|
|
m_body += convertStored(
|
2022-11-15 09:18:37 +00:00
|
|
|
firstArg.storedType(),
|
|
|
|
m_typeResolver->genericType(firstArg.storedType()),
|
|
|
|
registerVariable(argv));
|
|
|
|
m_body += u";\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
m_body += u" const QLoggingCategory *category = aotContext->resolveLoggingCategory(";
|
|
|
|
m_body += firstArgIsReference ? u"firstArg" : u"nullptr";
|
|
|
|
m_body += u", &firstArgIsCategory);\n";
|
|
|
|
m_body += u" if (category && category->isEnabled(" + type + u")) {\n";
|
|
|
|
|
|
|
|
m_body += u" const QString message = ";
|
|
|
|
if (argc > 0) {
|
2023-02-21 10:47:08 +00:00
|
|
|
const QString firstArgStringConversion = convertStored(
|
2022-11-15 09:18:37 +00:00
|
|
|
registerType(argv).storedType(),
|
|
|
|
m_typeResolver->stringType(), registerVariable(argv));
|
|
|
|
if (firstArgIsReference) {
|
|
|
|
m_body += u"(firstArgIsCategory ? QString() : (" + firstArgStringConversion;
|
|
|
|
if (argc > 1)
|
|
|
|
m_body += u".append(QLatin1Char(' ')))).append(";
|
|
|
|
else
|
|
|
|
m_body += u"))";
|
|
|
|
} else {
|
|
|
|
m_body += firstArgStringConversion;
|
|
|
|
if (argc > 1)
|
|
|
|
m_body += u".append(QLatin1Char(' ')).append(";
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
|
|
if (i > 1)
|
|
|
|
m_body += u".append(QLatin1Char(' ')).append("_s;
|
2023-02-21 10:47:08 +00:00
|
|
|
m_body += convertStored(
|
2022-11-15 09:18:37 +00:00
|
|
|
registerType(argv + i).storedType(),
|
2022-11-28 15:07:24 +00:00
|
|
|
m_typeResolver->stringType(), consumedRegisterVariable(argv + i)) + u')';
|
2022-11-15 09:18:37 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
m_body += u"QString()";
|
|
|
|
}
|
2023-08-15 08:30:44 +00:00
|
|
|
m_body += u";\n ";
|
|
|
|
generateSetInstructionPointer();
|
2022-11-15 09:18:37 +00:00
|
|
|
m_body += u" aotContext->writeToConsole(" + type + u", message, category);\n";
|
|
|
|
m_body += u" }\n";
|
|
|
|
m_body += u"}\n";
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-04-18 13:54:49 +00:00
|
|
|
bool QQmlJSCodeGenerator::inlineArrayMethod(const QString &name, int base, int argc, int argv)
|
|
|
|
{
|
|
|
|
const auto intType = m_typeResolver->int32Type();
|
|
|
|
const auto valueType = registerType(base).storedType()->valueType();
|
|
|
|
const auto boolType = m_typeResolver->boolType();
|
|
|
|
const auto stringType = m_typeResolver->stringType();
|
|
|
|
const auto baseType = registerType(base);
|
|
|
|
|
|
|
|
const QString baseVar = registerVariable(base);
|
|
|
|
const QString qjsListMethod = u"QJSList(&"_s + baseVar + u", aotContext->engine)."
|
|
|
|
+ name + u"(";
|
|
|
|
|
2023-11-03 08:45:46 +00:00
|
|
|
addInclude(u"QtQml/qjslist.h"_s);
|
2023-04-18 13:54:49 +00:00
|
|
|
|
|
|
|
if (name == u"includes" && argc > 0 && argc < 3) {
|
|
|
|
QString call = qjsListMethod
|
|
|
|
+ convertStored(registerType(argv).storedType(), valueType,
|
|
|
|
consumedRegisterVariable(argv));
|
|
|
|
if (argc == 2) {
|
|
|
|
call += u", " + convertStored(registerType(argv + 1).storedType(), intType,
|
|
|
|
consumedRegisterVariable(argv + 1));
|
|
|
|
}
|
|
|
|
call += u")";
|
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s
|
|
|
|
+ conversion(boolType, m_state.accumulatorOut(), call) + u";\n"_s;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name == u"toString" || (name == u"join" && argc < 2)) {
|
|
|
|
QString call = qjsListMethod;
|
|
|
|
if (argc == 1) {
|
|
|
|
call += convertStored(registerType(argv).storedType(), stringType,
|
|
|
|
consumedRegisterVariable(argv));
|
|
|
|
}
|
|
|
|
call += u")";
|
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s
|
|
|
|
+ conversion(stringType, m_state.accumulatorOut(), call) + u";\n"_s;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name == u"slice" && argc < 3) {
|
|
|
|
QString call = qjsListMethod;
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
|
|
|
if (i > 0)
|
|
|
|
call += u", ";
|
|
|
|
call += convertStored(registerType(argv + i).storedType(), intType,
|
|
|
|
consumedRegisterVariable(argv + i));
|
|
|
|
}
|
|
|
|
call += u")";
|
|
|
|
|
|
|
|
const auto outType = baseType.storedType()->isListProperty()
|
|
|
|
? m_typeResolver->globalType(m_typeResolver->qObjectListType())
|
|
|
|
: baseType;
|
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s
|
|
|
|
+ conversion(outType, m_state.accumulatorOut(), call) + u";\n"_s;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
|
|
|
|
QString call = qjsListMethod
|
|
|
|
+ convertStored(registerType(argv).storedType(), valueType,
|
|
|
|
consumedRegisterVariable(argv));
|
|
|
|
if (argc == 2) {
|
|
|
|
call += u", " + convertStored(registerType(argv + 1).storedType(), intType,
|
|
|
|
consumedRegisterVariable(argv + 1));
|
|
|
|
}
|
|
|
|
call += u")";
|
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s
|
|
|
|
+ conversion(intType, m_state.accumulatorOut(), call) + u";\n"_s;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
void QQmlJSCodeGenerator::generate_CallPropertyLookup(int index, int base, int argc, int argv)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_CallPropertyLookup);
|
|
|
|
|
2022-02-25 12:02:22 +00:00
|
|
|
if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptReturnValue)
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"call to untyped JavaScript function"_s);
|
2022-02-25 12:02:22 +00:00
|
|
|
|
2023-07-26 13:28:44 +00:00
|
|
|
const QQmlJSScope::ConstPtr scope = m_state.accumulatorOut().scopeType();
|
|
|
|
|
2022-02-16 15:34:56 +00:00
|
|
|
AccumulatorConverter registers(this);
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
const QQmlJSRegisterContent baseType = registerType(base);
|
2023-07-26 13:28:44 +00:00
|
|
|
const QString name = m_jsUnitGenerator->lookupName(index);
|
2022-06-03 08:51:26 +00:00
|
|
|
|
2023-07-26 13:28:44 +00:00
|
|
|
if (m_typeResolver->equals(scope, mathObject())) {
|
|
|
|
if (inlineMathMethod(name, argc, argv))
|
|
|
|
return;
|
|
|
|
} else if (m_typeResolver->equals(scope, consoleObject())) {
|
|
|
|
if (inlineConsoleMethod(name, argc, argv))
|
|
|
|
return;
|
|
|
|
} else if (m_typeResolver->equals(scope, m_typeResolver->stringType())) {
|
|
|
|
if (inlineStringMethod(name, base, argc, argv))
|
|
|
|
return;
|
|
|
|
} else if (baseType.storedType()->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
|
|
|
|
if (inlineArrayMethod(name, base, argc, argv))
|
|
|
|
return;
|
|
|
|
}
|
2023-04-18 13:54:49 +00:00
|
|
|
|
2023-07-26 13:28:44 +00:00
|
|
|
if (!scope->isReferenceType()) {
|
2023-04-28 12:02:20 +00:00
|
|
|
// This is possible, once we establish the right kind of lookup for it
|
|
|
|
reject(u"call to property '%1' of %2"_s.arg(name, baseType.descriptiveName()));
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2023-07-26 13:28:44 +00:00
|
|
|
const QString inputPointer = resolveQObjectPointer(
|
|
|
|
scope, baseType, registerVariable(base),
|
|
|
|
u"Cannot call method '%1' of %2"_s.arg(name));
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
const QString indexString = QString::number(index);
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"{\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
QString outVar;
|
|
|
|
m_body += argumentsList(argc, argv, &outVar);
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString lookup = u"aotContext->callObjectPropertyLookup("_s + indexString
|
2023-07-26 13:28:44 +00:00
|
|
|
+ u", "_s + inputPointer
|
2022-03-21 09:21:18 +00:00
|
|
|
+ u", args, types, "_s + QString::number(argc) + u')';
|
|
|
|
const QString initialization = u"aotContext->initCallObjectPropertyLookup("_s
|
2021-11-16 15:49:49 +00:00
|
|
|
+ indexString + u')';
|
|
|
|
generateLookup(lookup, initialization);
|
|
|
|
generateMoveOutVar(outVar);
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"}\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CallName(int name, int argc, int argv)
|
|
|
|
{
|
|
|
|
Q_UNUSED(name);
|
|
|
|
Q_UNUSED(argc);
|
|
|
|
Q_UNUSED(argv);
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"CallName"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CallPossiblyDirectEval(int argc, int argv)
|
|
|
|
{
|
|
|
|
Q_UNUSED(argc)
|
|
|
|
Q_UNUSED(argv)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CallGlobalLookup(int index, int argc, int argv)
|
|
|
|
{
|
|
|
|
Q_UNUSED(index);
|
|
|
|
Q_UNUSED(argc);
|
|
|
|
Q_UNUSED(argv);
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"CallGlobalLookup"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CallQmlContextPropertyLookup(int index, int argc, int argv)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_CallQmlContextPropertyLookup);
|
|
|
|
|
2022-02-25 12:02:22 +00:00
|
|
|
if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptReturnValue)
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"call to untyped JavaScript function"_s);
|
2022-02-25 12:02:22 +00:00
|
|
|
|
2022-05-25 13:46:13 +00:00
|
|
|
if (m_typeResolver->equals(m_state.accumulatorOut().scopeType(),
|
|
|
|
m_typeResolver->jsGlobalObject())) {
|
|
|
|
const QString name = m_jsUnitGenerator->stringForIndex(
|
|
|
|
m_jsUnitGenerator->lookupNameIndex(index));
|
|
|
|
if (inlineTranslateMethod(name, argc, argv))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-02-16 15:34:56 +00:00
|
|
|
AccumulatorConverter registers(this);
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
const QString indexString = QString::number(index);
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"{\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
QString outVar;
|
|
|
|
m_body += argumentsList(argc, argv, &outVar);
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString lookup = u"aotContext->callQmlContextPropertyLookup("_s + indexString
|
|
|
|
+ u", args, types, "_s + QString::number(argc) + u')';
|
|
|
|
const QString initialization = u"aotContext->initCallQmlContextPropertyLookup("_s
|
2021-11-16 15:49:49 +00:00
|
|
|
+ indexString + u')';
|
|
|
|
generateLookup(lookup, initialization);
|
|
|
|
generateMoveOutVar(outVar);
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"}\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CallWithSpread(int func, int thisObject, int argc, int argv)
|
|
|
|
{
|
|
|
|
Q_UNUSED(func)
|
|
|
|
Q_UNUSED(thisObject)
|
|
|
|
Q_UNUSED(argc)
|
|
|
|
Q_UNUSED(argv)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_TailCall(int func, int thisObject, int argc, int argv)
|
|
|
|
{
|
|
|
|
Q_UNUSED(func)
|
|
|
|
Q_UNUSED(thisObject)
|
|
|
|
Q_UNUSED(argc)
|
|
|
|
Q_UNUSED(argv)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_Construct(int func, int argc, int argv)
|
|
|
|
{
|
2023-07-27 13:10:31 +00:00
|
|
|
INJECT_TRACE_INFO(generate_Construct);
|
2021-11-16 15:49:49 +00:00
|
|
|
Q_UNUSED(func);
|
2023-07-27 13:10:31 +00:00
|
|
|
|
|
|
|
const auto original = m_typeResolver->original(m_state.accumulatorOut());
|
|
|
|
|
|
|
|
if (m_typeResolver->registerContains(original, m_typeResolver->dateTimeType())) {
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = ";
|
|
|
|
if (argc == 0) {
|
|
|
|
m_body += conversion(
|
|
|
|
m_typeResolver->dateTimeType(), m_state.accumulatorOut(),
|
|
|
|
u"QDateTime::currentDateTime()"_s) + u";\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc == 1
|
|
|
|
&& m_typeResolver->registerContains(
|
|
|
|
m_state.readRegister(argv), m_typeResolver->dateTimeType())) {
|
|
|
|
m_body += conversion(
|
|
|
|
registerType(argv), m_state.readRegister(argv), registerVariable(argv))
|
|
|
|
+ u";\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString ctorArgs;
|
|
|
|
constexpr int maxArgc = 7; // year, month, day, hours, minutes, seconds, milliseconds
|
|
|
|
for (int i = 0; i < std::min(argc, maxArgc); ++i) {
|
|
|
|
if (i > 0)
|
|
|
|
ctorArgs += u", ";
|
|
|
|
ctorArgs += conversion(
|
|
|
|
registerType(argv + i), m_state.readRegister(argv + i),
|
|
|
|
registerVariable(argv + i));
|
|
|
|
}
|
|
|
|
m_body += conversion(
|
|
|
|
m_typeResolver->dateTimeType(), m_state.accumulatorOut(),
|
|
|
|
u"aotContext->constructDateTime("_s + ctorArgs + u')') + u";\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-07-28 14:41:09 +00:00
|
|
|
if (m_typeResolver->registerContains(original, m_typeResolver->variantListType())) {
|
|
|
|
rejectIfBadArray();
|
|
|
|
|
|
|
|
if (argc == 1
|
|
|
|
&& m_typeResolver->registerContains(
|
|
|
|
m_state.readRegister(argv), m_typeResolver->realType())) {
|
2023-11-03 08:45:46 +00:00
|
|
|
addInclude(u"QtQml/qjslist.h"_s);
|
2023-07-28 14:41:09 +00:00
|
|
|
|
|
|
|
const QString error = u" aotContext->engine->throwError(QJSValue::RangeError, "_s
|
|
|
|
+ u"QLatin1String(\"Invalid array length\"));\n"_s
|
|
|
|
+ u" return "_s + errorReturnValue() + u";\n"_s;
|
|
|
|
|
|
|
|
const QString indexName = registerVariable(argv);
|
|
|
|
const auto indexType = registerType(argv);
|
|
|
|
if (!m_typeResolver->isIntegral(indexType)) {
|
|
|
|
m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s
|
|
|
|
+ error
|
|
|
|
+ u"}\n"_s;
|
|
|
|
} else if (!m_typeResolver->isUnsignedInteger(
|
|
|
|
m_typeResolver->containedType(indexType))) {
|
|
|
|
m_body += u"if ("_s + indexName + u" < 0) {\n"_s
|
|
|
|
+ error
|
|
|
|
+ u"}\n"_s;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s
|
|
|
|
+ m_state.accumulatorOut().storedType()->internalName() + u"();\n"_s;
|
|
|
|
m_body += u"QJSList(&"_s + m_state.accumulatorVariableOut
|
|
|
|
+ u", aotContext->engine).resize("_s
|
|
|
|
+ convertStored(
|
|
|
|
registerType(argv).storedType(), m_typeResolver->int32Type(),
|
|
|
|
consumedRegisterVariable(argv))
|
|
|
|
+ u");\n"_s;
|
|
|
|
} else if (!m_error->isValid()) {
|
|
|
|
generateArrayInitializer(argc, argv);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"Construct"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_ConstructWithSpread(int func, int argc, int argv)
|
|
|
|
{
|
|
|
|
Q_UNUSED(func)
|
|
|
|
Q_UNUSED(argc)
|
|
|
|
Q_UNUSED(argv)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_SetUnwindHandler(int offset)
|
|
|
|
{
|
|
|
|
Q_UNUSED(offset)
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"SetUnwindHandlerh"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_UnwindDispatch()
|
|
|
|
{
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"UnwindDispatch"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_UnwindToLabel(int level, int offset)
|
|
|
|
{
|
|
|
|
Q_UNUSED(level)
|
|
|
|
Q_UNUSED(offset)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_DeadTemporalZoneCheck(int name)
|
|
|
|
{
|
|
|
|
Q_UNUSED(name)
|
2023-10-24 14:30:28 +00:00
|
|
|
INJECT_TRACE_INFO(generate_DeadTemporalZoneCheck);
|
2023-04-17 12:12:05 +00:00
|
|
|
// Nothing to do here. If we have statically asserted the dtz check in the type propagator
|
|
|
|
// the value cannot be empty. Otherwise we can't get here.
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_ThrowException()
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_ThrowException);
|
|
|
|
|
|
|
|
generateSetInstructionPointer();
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"aotContext->engine->throwError("_s
|
2022-02-02 18:08:29 +00:00
|
|
|
+ conversion(m_state.accumulatorIn(), m_typeResolver->globalType(
|
2021-11-16 15:49:49 +00:00
|
|
|
m_typeResolver->jsValueType()),
|
2022-03-21 09:21:18 +00:00
|
|
|
m_state.accumulatorVariableIn) + u");\n"_s;
|
|
|
|
m_body += u"return "_s + errorReturnValue() + u";\n"_s;
|
2022-06-27 14:26:43 +00:00
|
|
|
m_skipUntilNextLabel = true;
|
|
|
|
resetState();
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_GetException()
|
|
|
|
{
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_SetException()
|
|
|
|
{
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CreateCallContext()
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_CreateCallContext);
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"{\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_PushCatchContext(int index, int nameIndex)
|
|
|
|
{
|
|
|
|
Q_UNUSED(index)
|
|
|
|
Q_UNUSED(nameIndex)
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"PushCatchContext"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_PushWithContext()
|
|
|
|
{
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_PushBlockContext(int index)
|
|
|
|
{
|
|
|
|
Q_UNUSED(index)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CloneBlockContext()
|
|
|
|
{
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_PushScriptContext(int index)
|
|
|
|
{
|
|
|
|
Q_UNUSED(index)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_PopScriptContext()
|
|
|
|
{
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_PopContext()
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_PopContext);
|
|
|
|
|
|
|
|
// Add a semicolon before the closing brace, in case there was a bare label before it.
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";}\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_GetIterator(int iterator)
|
|
|
|
{
|
2023-09-12 10:16:47 +00:00
|
|
|
INJECT_TRACE_INFO(generate_GetIterator);
|
|
|
|
|
2023-11-03 08:45:46 +00:00
|
|
|
addInclude(u"QtQml/qjslist.h"_s);
|
2023-09-12 10:16:47 +00:00
|
|
|
const QQmlJSRegisterContent listType = m_state.accumulatorIn();
|
|
|
|
if (!listType.isList())
|
|
|
|
reject(u"iterator on non-list type"_s);
|
|
|
|
|
|
|
|
const QQmlJSRegisterContent iteratorType = m_state.accumulatorOut();
|
2023-09-26 13:02:57 +00:00
|
|
|
if (!iteratorType.isProperty()) {
|
|
|
|
reject(u"using non-iterator as iterator"_s);
|
|
|
|
return;
|
|
|
|
}
|
2023-09-12 10:16:47 +00:00
|
|
|
|
|
|
|
const QString identifier = QString::number(iteratorType.baseLookupIndex());
|
|
|
|
const QString iteratorName = m_state.accumulatorVariableOut + u"Iterator" + identifier;
|
|
|
|
const QString listName = m_state.accumulatorVariableOut + u"List" + identifier;
|
|
|
|
|
|
|
|
m_body += u"QJSListFor"_s
|
|
|
|
+ (iterator == int(QQmlJS::AST::ForEachType::In) ? u"In"_s : u"Of"_s)
|
|
|
|
+ u"Iterator "_s + iteratorName + u";\n";
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = &" + iteratorName + u";\n";
|
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut + u"->init(";
|
|
|
|
if (iterator == int(QQmlJS::AST::ForEachType::In)) {
|
|
|
|
if (!m_typeResolver->equals(iteratorType.storedType(), m_typeResolver->forInIteratorPtr()))
|
|
|
|
reject(u"using non-iterator as iterator"_s);
|
|
|
|
m_body += u"QJSList(&" + m_state.accumulatorVariableIn + u", aotContext->engine)";
|
|
|
|
}
|
|
|
|
m_body += u");\n";
|
|
|
|
|
|
|
|
if (iterator == int(QQmlJS::AST::ForEachType::Of)) {
|
|
|
|
if (!m_typeResolver->equals(iteratorType.storedType(), m_typeResolver->forOfIteratorPtr()))
|
|
|
|
reject(u"using non-iterator as iterator"_s);
|
|
|
|
m_body += u"const auto &" // Rely on life time extension for const refs
|
|
|
|
+ listName + u" = " + consumedAccumulatorVariableIn();
|
|
|
|
}
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
V4: Eliminate "done" from iterators
Instead of dragging another stack value around to mark if the iterator
was done, rather pass it an offset it should jump to if so. It can then
jump over any IteratorClose instruction while the ExceptionHandler can
still point to the IteratorClose instruction.
For this to work, we also have to refrain from checking for exceptions
as part of IteratorNext or IteratorClose. If IteratorNext generates an
exception, it also jumps to the "done" label, after which we dispatch
the exception. We don't want to jump to the exception handler for other
instructions in between as that would close the iterator. The iterator
should _not_ be closed if it has just thrown an exception, though. The
same holds for IteratorClose: If it throws an exception, we don't want
to jump back to the beginning of the loop's exception handler, since
that would produce an infinite loop. We also don't want to reset the
exception handler before IteratorClose because it needs to also be reset
if the iterator does not need to be closed.
This saves quite a few instructions and stack variables on actual
iteration.
For destructuring, we have to change the execution flow a bit. We need
to first perform the iteration for non-rest parameters, saving the
results in separate stack slots. This way we can apply our new "jump if
done" behavior if the iterator runs out or produces an exception itself.
We then save the "done" state in a separate stack slot, as before.
During the assignment of the iteration results to the actual variables,
we install an exception handler, so that we can still close the iterator
if one of the initializers throws an exception. This produces a few more
instructions than before:
1. We need to set and read the "needsClose" variable explicitly rather
than having IteratorNext and IteratorDone do it implicitly.
2. We need an additional CheckException after the iteration.
3. We need an additional conditional Jump over the IteratorDone.
Everything considered, the savings we get for regular iteration and the
more consistent semantics of the instructions involved are well worth
the few extra instructions on destructuring, especially since everything
those extra instructions do was done implicitly by the iterator
instructions before.
For consistency, the IteratorNextForYieldStar instruction is refactored
to work the same way as IteratorNext: In case of either an exception or
"done" it jumps to an offset, and we refrain from individually
exception-checking each IteratorNextForYieldStart instruction.
Task-number: QTBUG-116725
Change-Id: I9e2ad4319495aecabafdbbd3dd0cbf3c6191f942
Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
2023-08-25 13:40:37 +00:00
|
|
|
void QQmlJSCodeGenerator::generate_IteratorNext(int value, int offset)
|
2021-11-16 15:49:49 +00:00
|
|
|
{
|
2023-09-12 10:16:47 +00:00
|
|
|
INJECT_TRACE_INFO(generate_IteratorNext);
|
|
|
|
|
|
|
|
Q_ASSERT(value == m_state.changedRegisterIndex());
|
|
|
|
const QQmlJSRegisterContent iteratorContent = m_state.accumulatorIn();
|
2023-09-26 13:02:57 +00:00
|
|
|
if (!iteratorContent.isProperty()) {
|
|
|
|
reject(u"using non-iterator as iterator"_s);
|
|
|
|
return;
|
|
|
|
}
|
2023-09-12 10:16:47 +00:00
|
|
|
|
2023-09-26 13:02:57 +00:00
|
|
|
const QQmlJSScope::ConstPtr iteratorType = iteratorContent.storedType();
|
2023-09-12 10:16:47 +00:00
|
|
|
const QString iteratorTypeName = iteratorType->internalName();
|
|
|
|
const QString listName = m_state.accumulatorVariableIn
|
|
|
|
+ u"List" + QString::number(iteratorContent.baseLookupIndex());
|
|
|
|
QString qjsList;
|
|
|
|
if (m_typeResolver->equals(iteratorType, m_typeResolver->forOfIteratorPtr()))
|
|
|
|
qjsList = u"QJSList(&" + listName + u", aotContext->engine)";
|
|
|
|
else if (!m_typeResolver->equals(iteratorType, m_typeResolver->forInIteratorPtr()))
|
|
|
|
reject(u"using non-iterator as iterator"_s);
|
|
|
|
|
|
|
|
m_body += u"if (" + m_state.accumulatorVariableIn + u"->hasNext(" + qjsList + u")) {\n ";
|
|
|
|
m_body += changedRegisterVariable() + u" = "
|
|
|
|
+ conversion(
|
|
|
|
m_typeResolver->valueType(iteratorContent),
|
|
|
|
m_state.changedRegister(),
|
|
|
|
m_state.accumulatorVariableIn + u"->next(" + qjsList + u')')
|
|
|
|
+ u";\n";
|
|
|
|
m_body += u"} else {\n ";
|
|
|
|
m_body += changedRegisterVariable() + u" = "
|
|
|
|
+ conversion(m_typeResolver->voidType(), m_state.changedRegister(), QString());
|
|
|
|
m_body += u";\n ";
|
|
|
|
generateJumpCodeWithTypeConversions(offset);
|
|
|
|
m_body += u"\n}"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
V4: Eliminate "done" from iterators
Instead of dragging another stack value around to mark if the iterator
was done, rather pass it an offset it should jump to if so. It can then
jump over any IteratorClose instruction while the ExceptionHandler can
still point to the IteratorClose instruction.
For this to work, we also have to refrain from checking for exceptions
as part of IteratorNext or IteratorClose. If IteratorNext generates an
exception, it also jumps to the "done" label, after which we dispatch
the exception. We don't want to jump to the exception handler for other
instructions in between as that would close the iterator. The iterator
should _not_ be closed if it has just thrown an exception, though. The
same holds for IteratorClose: If it throws an exception, we don't want
to jump back to the beginning of the loop's exception handler, since
that would produce an infinite loop. We also don't want to reset the
exception handler before IteratorClose because it needs to also be reset
if the iterator does not need to be closed.
This saves quite a few instructions and stack variables on actual
iteration.
For destructuring, we have to change the execution flow a bit. We need
to first perform the iteration for non-rest parameters, saving the
results in separate stack slots. This way we can apply our new "jump if
done" behavior if the iterator runs out or produces an exception itself.
We then save the "done" state in a separate stack slot, as before.
During the assignment of the iteration results to the actual variables,
we install an exception handler, so that we can still close the iterator
if one of the initializers throws an exception. This produces a few more
instructions than before:
1. We need to set and read the "needsClose" variable explicitly rather
than having IteratorNext and IteratorDone do it implicitly.
2. We need an additional CheckException after the iteration.
3. We need an additional conditional Jump over the IteratorDone.
Everything considered, the savings we get for regular iteration and the
more consistent semantics of the instructions involved are well worth
the few extra instructions on destructuring, especially since everything
those extra instructions do was done implicitly by the iterator
instructions before.
For consistency, the IteratorNextForYieldStar instruction is refactored
to work the same way as IteratorNext: In case of either an exception or
"done" it jumps to an offset, and we refrain from individually
exception-checking each IteratorNextForYieldStart instruction.
Task-number: QTBUG-116725
Change-Id: I9e2ad4319495aecabafdbbd3dd0cbf3c6191f942
Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
2023-08-25 13:40:37 +00:00
|
|
|
void QQmlJSCodeGenerator::generate_IteratorNextForYieldStar(int iterator, int object, int offset)
|
2021-11-16 15:49:49 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(iterator)
|
|
|
|
Q_UNUSED(object)
|
V4: Eliminate "done" from iterators
Instead of dragging another stack value around to mark if the iterator
was done, rather pass it an offset it should jump to if so. It can then
jump over any IteratorClose instruction while the ExceptionHandler can
still point to the IteratorClose instruction.
For this to work, we also have to refrain from checking for exceptions
as part of IteratorNext or IteratorClose. If IteratorNext generates an
exception, it also jumps to the "done" label, after which we dispatch
the exception. We don't want to jump to the exception handler for other
instructions in between as that would close the iterator. The iterator
should _not_ be closed if it has just thrown an exception, though. The
same holds for IteratorClose: If it throws an exception, we don't want
to jump back to the beginning of the loop's exception handler, since
that would produce an infinite loop. We also don't want to reset the
exception handler before IteratorClose because it needs to also be reset
if the iterator does not need to be closed.
This saves quite a few instructions and stack variables on actual
iteration.
For destructuring, we have to change the execution flow a bit. We need
to first perform the iteration for non-rest parameters, saving the
results in separate stack slots. This way we can apply our new "jump if
done" behavior if the iterator runs out or produces an exception itself.
We then save the "done" state in a separate stack slot, as before.
During the assignment of the iteration results to the actual variables,
we install an exception handler, so that we can still close the iterator
if one of the initializers throws an exception. This produces a few more
instructions than before:
1. We need to set and read the "needsClose" variable explicitly rather
than having IteratorNext and IteratorDone do it implicitly.
2. We need an additional CheckException after the iteration.
3. We need an additional conditional Jump over the IteratorDone.
Everything considered, the savings we get for regular iteration and the
more consistent semantics of the instructions involved are well worth
the few extra instructions on destructuring, especially since everything
those extra instructions do was done implicitly by the iterator
instructions before.
For consistency, the IteratorNextForYieldStar instruction is refactored
to work the same way as IteratorNext: In case of either an exception or
"done" it jumps to an offset, and we refrain from individually
exception-checking each IteratorNextForYieldStart instruction.
Task-number: QTBUG-116725
Change-Id: I9e2ad4319495aecabafdbbd3dd0cbf3c6191f942
Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
2023-08-25 13:40:37 +00:00
|
|
|
Q_UNUSED(offset)
|
2021-11-16 15:49:49 +00:00
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
V4: Eliminate "done" from iterators
Instead of dragging another stack value around to mark if the iterator
was done, rather pass it an offset it should jump to if so. It can then
jump over any IteratorClose instruction while the ExceptionHandler can
still point to the IteratorClose instruction.
For this to work, we also have to refrain from checking for exceptions
as part of IteratorNext or IteratorClose. If IteratorNext generates an
exception, it also jumps to the "done" label, after which we dispatch
the exception. We don't want to jump to the exception handler for other
instructions in between as that would close the iterator. The iterator
should _not_ be closed if it has just thrown an exception, though. The
same holds for IteratorClose: If it throws an exception, we don't want
to jump back to the beginning of the loop's exception handler, since
that would produce an infinite loop. We also don't want to reset the
exception handler before IteratorClose because it needs to also be reset
if the iterator does not need to be closed.
This saves quite a few instructions and stack variables on actual
iteration.
For destructuring, we have to change the execution flow a bit. We need
to first perform the iteration for non-rest parameters, saving the
results in separate stack slots. This way we can apply our new "jump if
done" behavior if the iterator runs out or produces an exception itself.
We then save the "done" state in a separate stack slot, as before.
During the assignment of the iteration results to the actual variables,
we install an exception handler, so that we can still close the iterator
if one of the initializers throws an exception. This produces a few more
instructions than before:
1. We need to set and read the "needsClose" variable explicitly rather
than having IteratorNext and IteratorDone do it implicitly.
2. We need an additional CheckException after the iteration.
3. We need an additional conditional Jump over the IteratorDone.
Everything considered, the savings we get for regular iteration and the
more consistent semantics of the instructions involved are well worth
the few extra instructions on destructuring, especially since everything
those extra instructions do was done implicitly by the iterator
instructions before.
For consistency, the IteratorNextForYieldStar instruction is refactored
to work the same way as IteratorNext: In case of either an exception or
"done" it jumps to an offset, and we refrain from individually
exception-checking each IteratorNextForYieldStart instruction.
Task-number: QTBUG-116725
Change-Id: I9e2ad4319495aecabafdbbd3dd0cbf3c6191f942
Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
2023-08-25 13:40:37 +00:00
|
|
|
void QQmlJSCodeGenerator::generate_IteratorClose()
|
2021-11-16 15:49:49 +00:00
|
|
|
{
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_DestructureRestElement()
|
|
|
|
{
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_DeleteProperty(int base, int index)
|
|
|
|
{
|
|
|
|
Q_UNUSED(base)
|
|
|
|
Q_UNUSED(index)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_DeleteName(int name)
|
|
|
|
{
|
|
|
|
Q_UNUSED(name)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_TypeofName(int name)
|
|
|
|
{
|
|
|
|
Q_UNUSED(name);
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"TypeofName"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_TypeofValue()
|
|
|
|
{
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"TypeofValue"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_DeclareVar(int varName, int isDeletable)
|
|
|
|
{
|
|
|
|
Q_UNUSED(varName)
|
|
|
|
Q_UNUSED(isDeletable)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_DefineArray(int argc, int args)
|
|
|
|
{
|
2022-02-24 07:57:44 +00:00
|
|
|
INJECT_TRACE_INFO(generate_DefineArray);
|
2022-02-16 13:01:52 +00:00
|
|
|
|
2023-07-28 14:41:09 +00:00
|
|
|
rejectIfBadArray();
|
|
|
|
if (!m_error->isValid())
|
|
|
|
generateArrayInitializer(argc, args);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
|
|
|
|
{
|
2023-06-30 08:14:17 +00:00
|
|
|
INJECT_TRACE_INFO(generate_DefineObjectLiteral);
|
|
|
|
|
|
|
|
const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
|
|
|
|
if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Value) {
|
|
|
|
reject(u"storing an object literal in a non-value type"_s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(m_state.accumulatorOut());
|
|
|
|
|
2023-08-24 14:02:00 +00:00
|
|
|
const int classSize = m_jsUnitGenerator->jsClassSize(internalClassId);
|
|
|
|
Q_ASSERT(argc >= classSize);
|
|
|
|
|
|
|
|
if (m_typeResolver->equals(contained, m_typeResolver->varType())
|
|
|
|
|| m_typeResolver->equals(contained, m_typeResolver->variantMapType())) {
|
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = QVariantMap {\n";
|
|
|
|
const QQmlJSScope::ConstPtr propType = m_typeResolver->varType();
|
|
|
|
for (int i = 0; i < classSize; ++i) {
|
|
|
|
m_body += u"{ "_s
|
|
|
|
+ QQmlJSUtils::toLiteral(m_jsUnitGenerator->jsClassMember(internalClassId, i))
|
|
|
|
+ u", "_s;
|
|
|
|
const int currentArg = args + i;
|
|
|
|
const QQmlJSScope::ConstPtr argType = registerType(currentArg).storedType();
|
|
|
|
const QString consumedArg = consumedRegisterVariable(currentArg);
|
|
|
|
m_body += convertStored(argType, propType, consumedArg) + u" },\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = classSize; i < argc; i += 3) {
|
|
|
|
const int nameArg = argc + i + 1;
|
|
|
|
m_body += u"{ "_s
|
|
|
|
+ conversion(
|
|
|
|
registerType(nameArg),
|
|
|
|
m_typeResolver->globalType(m_typeResolver->stringType()),
|
|
|
|
consumedRegisterVariable(nameArg))
|
|
|
|
+ u", "_s;
|
|
|
|
|
|
|
|
const int valueArg = argc + i + 2;
|
|
|
|
m_body += convertStored(
|
|
|
|
registerType(valueArg).storedType(),
|
|
|
|
propType,
|
|
|
|
consumedRegisterVariable(valueArg))
|
|
|
|
+ u" },\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
m_body += u"};\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-06-30 08:14:17 +00:00
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s + stored->augmentedInternalName();
|
|
|
|
const bool isVariantOrPrimitive = m_typeResolver->equals(stored, m_typeResolver->varType())
|
|
|
|
|| m_typeResolver->equals(stored, m_typeResolver->jsPrimitiveType());
|
|
|
|
|
|
|
|
if (m_typeResolver->registerContains(m_state.accumulatorOut(), stored)) {
|
|
|
|
m_body += u"()";
|
|
|
|
} else if (isVariantOrPrimitive) {
|
|
|
|
m_body += u'(' + metaType(m_typeResolver->containedType(m_state.accumulatorOut())) + u')';
|
|
|
|
} else {
|
|
|
|
reject(u"storing an object literal in an unsupported container %1"_s
|
|
|
|
.arg(stored->internalName()));
|
|
|
|
}
|
|
|
|
m_body += u";\n";
|
|
|
|
|
2023-08-24 14:02:00 +00:00
|
|
|
if (argc == 0)
|
|
|
|
return;
|
|
|
|
|
2023-06-30 08:14:17 +00:00
|
|
|
bool isExtension = false;
|
|
|
|
if (!m_typeResolver->canPopulate(contained, m_typeResolver->variantMapType(), &isExtension)) {
|
|
|
|
reject(u"storing an object literal in a non-structured value type"_s);
|
|
|
|
}
|
|
|
|
|
|
|
|
const QQmlJSScope::ConstPtr accessor = isExtension
|
|
|
|
? contained->extensionType().scope
|
|
|
|
: contained;
|
|
|
|
|
|
|
|
m_body += u"{\n";
|
|
|
|
m_body += u" const QMetaObject *meta = ";
|
|
|
|
if (!isExtension && isVariantOrPrimitive)
|
|
|
|
m_body += m_state.accumulatorVariableOut + u".metaType().metaObject()";
|
|
|
|
else
|
|
|
|
m_body += metaObject(accessor);
|
|
|
|
m_body += u";\n";
|
|
|
|
|
|
|
|
for (int i = 0; i < classSize; ++i) {
|
|
|
|
m_body += u" {\n";
|
|
|
|
const QString propName = m_jsUnitGenerator->jsClassMember(internalClassId, i);
|
|
|
|
const int currentArg = args + i;
|
|
|
|
const QQmlJSRegisterContent propType = m_state.readRegister(currentArg);
|
|
|
|
const QQmlJSRegisterContent argType = registerType(currentArg);
|
|
|
|
const QQmlJSMetaProperty property = contained->property(propName);
|
|
|
|
const QString consumedArg = consumedRegisterVariable(currentArg);
|
|
|
|
QString argument = conversion(argType, propType, consumedArg);
|
|
|
|
|
|
|
|
if (argument == consumedArg) {
|
|
|
|
argument = registerVariable(currentArg);
|
|
|
|
} else {
|
|
|
|
m_body += u" auto arg = "_s + argument + u";\n";
|
|
|
|
argument = u"arg"_s;
|
|
|
|
}
|
|
|
|
|
|
|
|
int index = property.index();
|
|
|
|
if (index == -1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
m_body += u" void *argv[] = { %1, nullptr };\n"_s
|
|
|
|
.arg(contentPointer(propType, argument));
|
|
|
|
m_body += u" meta->d.static_metacall(reinterpret_cast<QObject *>(";
|
|
|
|
m_body += contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut);
|
|
|
|
m_body += u"), QMetaObject::WriteProperty, ";
|
|
|
|
m_body += QString::number(index) + u", argv);\n";
|
|
|
|
m_body += u" }\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is not implemented because we cannot statically determine the type of the value and we
|
|
|
|
// don't want to rely on QVariant::convert() since that may give different results than
|
|
|
|
// the JavaScript coercion. We might still make it work by querying the QMetaProperty
|
|
|
|
// for its type at run time and runtime coercing to that, but we don't know whether that
|
|
|
|
// still pays off.
|
|
|
|
if (argc > classSize)
|
|
|
|
reject(u"non-literal keys of object literals"_s);
|
|
|
|
|
|
|
|
m_body += u"}\n";
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CreateClass(int classIndex, int heritage, int computedNames)
|
|
|
|
{
|
|
|
|
Q_UNUSED(classIndex)
|
|
|
|
Q_UNUSED(heritage)
|
|
|
|
Q_UNUSED(computedNames)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CreateMappedArgumentsObject()
|
|
|
|
{
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CreateUnmappedArgumentsObject()
|
|
|
|
{
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CreateRestParameter(int argIndex)
|
|
|
|
{
|
|
|
|
Q_UNUSED(argIndex)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_ConvertThisToObject()
|
|
|
|
{
|
2023-05-04 14:51:47 +00:00
|
|
|
INJECT_TRACE_INFO(generate_ConvertThisToObject);
|
|
|
|
|
2023-05-04 13:39:57 +00:00
|
|
|
m_body += changedRegisterVariable() + u" = "_s
|
|
|
|
+ conversion(m_typeResolver->qObjectType(), m_state.changedRegister(),
|
|
|
|
u"aotContext->thisObject()"_s)
|
|
|
|
+ u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_LoadSuperConstructor()
|
|
|
|
{
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_ToObject()
|
|
|
|
{
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_Jump(int offset)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_Jump);
|
|
|
|
|
2022-02-17 12:04:35 +00:00
|
|
|
generateJumpCodeWithTypeConversions(offset);
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
m_skipUntilNextLabel = true;
|
2022-06-27 14:26:43 +00:00
|
|
|
resetState();
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_JumpTrue(int offset)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_JumpTrue);
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"if ("_s;
|
2023-02-21 10:47:08 +00:00
|
|
|
m_body += convertStored(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
|
|
|
|
m_state.accumulatorVariableIn);
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u") "_s;
|
2022-02-17 12:04:35 +00:00
|
|
|
generateJumpCodeWithTypeConversions(offset);
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_JumpFalse(int offset)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_JumpFalse);
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"if (!"_s;
|
2023-02-21 10:47:08 +00:00
|
|
|
m_body += convertStored(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
|
|
|
|
m_state.accumulatorVariableIn);
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u") "_s;
|
2022-02-17 12:04:35 +00:00
|
|
|
generateJumpCodeWithTypeConversions(offset);
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_JumpNoException(int offset)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_JumpNoException);
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"if (!context->engine->hasException()) "_s;
|
2022-02-17 12:04:35 +00:00
|
|
|
generateJumpCodeWithTypeConversions(offset);
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_JumpNotUndefined(int offset)
|
|
|
|
{
|
|
|
|
Q_UNUSED(offset)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CheckException()
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_CheckException);
|
|
|
|
|
|
|
|
generateExceptionCheck();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CmpEqNull()
|
|
|
|
{
|
2023-11-16 10:02:14 +00:00
|
|
|
INJECT_TRACE_INFO(generate_CmpEqNull);
|
2023-08-25 10:04:18 +00:00
|
|
|
generateEqualityOperation(
|
|
|
|
m_typeResolver->globalType(m_typeResolver->nullType()), QString(), u"equals"_s, false);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CmpNeNull()
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_CmlNeNull);
|
2023-08-25 10:04:18 +00:00
|
|
|
generateEqualityOperation(
|
|
|
|
m_typeResolver->globalType(m_typeResolver->nullType()), QString(), u"equals"_s, true);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QString QQmlJSCodeGenerator::getLookupPreparation(
|
|
|
|
const QQmlJSRegisterContent &content, const QString &var, int lookup)
|
|
|
|
{
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->registerContains(content, content.storedType()))
|
2021-11-16 15:49:49 +00:00
|
|
|
return QString();
|
2022-02-04 21:35:23 +00:00
|
|
|
|
|
|
|
if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())) {
|
2022-03-21 09:21:18 +00:00
|
|
|
return var + u" = QVariant(aotContext->lookupResultMetaType("_s
|
|
|
|
+ QString::number(lookup) + u"))"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
// TODO: We could make sure they're compatible, for example QObject pointers.
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QQmlJSCodeGenerator::contentPointer(const QQmlJSRegisterContent &content, const QString &var)
|
|
|
|
{
|
|
|
|
const QQmlJSScope::ConstPtr stored = content.storedType();
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->registerContains(content, stored))
|
2021-11-16 15:49:49 +00:00
|
|
|
return u'&' + var;
|
2022-07-04 15:00:15 +00:00
|
|
|
|
2023-03-14 13:51:36 +00:00
|
|
|
if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())
|
|
|
|
|| m_typeResolver->registerIsStoredIn(content, m_typeResolver->jsPrimitiveType())) {
|
2022-03-21 09:21:18 +00:00
|
|
|
return var + u".data()"_s;
|
2023-03-14 13:51:36 +00:00
|
|
|
}
|
2022-07-04 15:00:15 +00:00
|
|
|
|
|
|
|
if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
|
2021-11-16 15:49:49 +00:00
|
|
|
return u'&' + var;
|
2022-07-04 15:00:15 +00:00
|
|
|
|
2023-03-27 16:00:46 +00:00
|
|
|
if (m_typeResolver->isNumeric(content.storedType())
|
2023-05-05 07:30:27 +00:00
|
|
|
&& m_typeResolver->containedType(content)->scopeType() == QQmlSA::ScopeType::EnumScope) {
|
2022-07-04 15:00:15 +00:00
|
|
|
return u'&' + var;
|
|
|
|
}
|
|
|
|
|
2022-10-19 12:35:24 +00:00
|
|
|
if (stored->isListProperty() && m_typeResolver->containedType(content)->isListProperty())
|
|
|
|
return u'&' + var;
|
|
|
|
|
2023-03-14 13:51:36 +00:00
|
|
|
reject(u"content pointer of unsupported wrapper type "_s + content.descriptiveName());
|
2021-11-16 15:49:49 +00:00
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QQmlJSCodeGenerator::contentType(const QQmlJSRegisterContent &content, const QString &var)
|
|
|
|
{
|
|
|
|
const QQmlJSScope::ConstPtr stored = content.storedType();
|
|
|
|
const QQmlJSScope::ConstPtr contained = QQmlJSScope::nonCompositeBaseType(
|
|
|
|
m_typeResolver->containedType(content));
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(contained, stored))
|
2021-11-16 15:49:49 +00:00
|
|
|
return metaTypeFromType(stored);
|
2022-07-04 15:00:15 +00:00
|
|
|
|
2023-03-14 13:51:36 +00:00
|
|
|
if (m_typeResolver->equals(stored, m_typeResolver->varType())
|
|
|
|
|| m_typeResolver->registerIsStoredIn(content, m_typeResolver->jsPrimitiveType())) {
|
|
|
|
return var + u".metaType()"_s; // We expect the container to be initialized
|
|
|
|
}
|
2022-07-04 15:00:15 +00:00
|
|
|
|
|
|
|
if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
|
2021-11-16 15:49:49 +00:00
|
|
|
return metaTypeFromName(contained);
|
2022-07-04 15:00:15 +00:00
|
|
|
|
2023-05-05 07:30:27 +00:00
|
|
|
if (m_typeResolver->isNumeric(stored) && contained->scopeType() == QQmlSA::ScopeType::EnumScope)
|
2023-03-27 16:00:46 +00:00
|
|
|
return metaTypeFromType(contained->baseType());
|
2022-07-04 15:00:15 +00:00
|
|
|
|
2022-10-19 12:35:24 +00:00
|
|
|
if (stored->isListProperty() && m_typeResolver->containedType(content)->isListProperty())
|
|
|
|
return metaTypeFromType(m_typeResolver->listPropertyType());
|
|
|
|
|
2023-03-14 13:51:36 +00:00
|
|
|
reject(u"content type of unsupported wrapper type "_s + content.descriptiveName());
|
2021-11-16 15:49:49 +00:00
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CmpEqInt(int lhsConst)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_CmpEqInt);
|
|
|
|
|
2023-08-25 10:04:18 +00:00
|
|
|
generateEqualityOperation(
|
|
|
|
m_typeResolver->globalType(m_typeResolver->int32Type()), QString::number(lhsConst),
|
|
|
|
u"equals"_s, false);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CmpNeInt(int lhsConst)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_CmpNeInt);
|
|
|
|
|
2023-08-25 10:04:18 +00:00
|
|
|
generateEqualityOperation(
|
|
|
|
m_typeResolver->globalType(m_typeResolver->int32Type()), QString::number(lhsConst),
|
|
|
|
u"equals"_s, true);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CmpEq(int lhs)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_CmpEq);
|
2023-08-25 10:04:18 +00:00
|
|
|
generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"equals"_s, false);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CmpNe(int lhs)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_CmpNe);
|
2023-08-25 10:04:18 +00:00
|
|
|
generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"equals"_s, true);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CmpGt(int lhs)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_CmpGt);
|
2022-03-21 09:21:18 +00:00
|
|
|
generateCompareOperation(lhs, u">"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CmpGe(int lhs)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_CmpGe);
|
2022-03-21 09:21:18 +00:00
|
|
|
generateCompareOperation(lhs, u">="_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CmpLt(int lhs)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_CmpLt);
|
2022-03-21 09:21:18 +00:00
|
|
|
generateCompareOperation(lhs, u"<"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CmpLe(int lhs)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_CmpLe);
|
2022-03-21 09:21:18 +00:00
|
|
|
generateCompareOperation(lhs, u"<="_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CmpStrictEqual(int lhs)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_CmpStrictEqual);
|
2023-08-25 10:04:18 +00:00
|
|
|
generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"strictlyEquals"_s, false);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CmpStrictNotEqual(int lhs)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_CmpStrictNotEqual);
|
2023-08-25 10:04:18 +00:00
|
|
|
generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"strictlyEquals"_s, true);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CmpIn(int lhs)
|
|
|
|
{
|
|
|
|
Q_UNUSED(lhs)
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"CmpIn"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_CmpInstanceOf(int lhs)
|
|
|
|
{
|
|
|
|
Q_UNUSED(lhs)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_As(int lhs)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_As);
|
|
|
|
|
2022-02-17 12:04:35 +00:00
|
|
|
const QString input = registerVariable(lhs);
|
2023-02-16 09:54:34 +00:00
|
|
|
const QQmlJSRegisterContent inputContent = m_state.readRegister(lhs);
|
|
|
|
const QQmlJSRegisterContent outputContent = m_state.accumulatorOut();
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2023-02-23 14:59:44 +00:00
|
|
|
// If the original output is a conversion, we're supposed to check for the contained
|
|
|
|
// type and if it doesn't match, set the result to null or undefined.
|
|
|
|
const QQmlJSRegisterContent originalContent = m_typeResolver->original(outputContent);
|
|
|
|
|
|
|
|
const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(originalContent);
|
2023-11-01 13:12:53 +00:00
|
|
|
const bool isTrivial = m_typeResolver->inherits(
|
|
|
|
m_typeResolver->originalContainedType(inputContent), contained);
|
2023-02-23 14:59:44 +00:00
|
|
|
|
2023-11-01 13:12:53 +00:00
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s;
|
|
|
|
|
|
|
|
if (contained->isReferenceType() && !isTrivial) {
|
2023-03-17 09:46:42 +00:00
|
|
|
const QQmlJSScope::ConstPtr genericContained = m_typeResolver->genericType(contained);
|
|
|
|
const QString inputConversion = inputContent.storedType()->isReferenceType()
|
|
|
|
? input
|
|
|
|
: convertStored(inputContent.storedType(), genericContained, input);
|
|
|
|
|
2023-11-16 15:22:38 +00:00
|
|
|
if (contained->isComposite() && m_typeResolver->equals(
|
|
|
|
m_state.accumulatorIn().storedType(), m_typeResolver->metaObjectType())) {
|
2023-02-16 09:54:34 +00:00
|
|
|
m_body += conversion(
|
2023-03-17 09:46:42 +00:00
|
|
|
genericContained, outputContent,
|
|
|
|
m_state.accumulatorVariableIn + u"->cast("_s + inputConversion + u')');
|
2023-02-16 09:54:34 +00:00
|
|
|
} else {
|
|
|
|
m_body += conversion(
|
2023-03-17 09:46:42 +00:00
|
|
|
genericContained, outputContent,
|
|
|
|
u'(' + metaObject(contained) + u")->cast("_s + inputConversion + u')');
|
2023-02-16 09:54:34 +00:00
|
|
|
}
|
|
|
|
m_body += u";\n"_s;
|
|
|
|
return;
|
2023-11-01 13:12:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m_typeResolver->equals(inputContent.storedType(), m_typeResolver->varType())) {
|
2023-11-02 13:46:45 +00:00
|
|
|
if (const auto target = m_typeResolver->extractNonVoidFromOptionalType(originalContent)) {
|
2023-02-16 09:54:34 +00:00
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s;
|
|
|
|
m_body += input + u".metaType() == "_s + metaType(target)
|
|
|
|
+ u" ? " + conversion(inputContent, outputContent, input)
|
|
|
|
+ u" : " + conversion(m_typeResolver->globalType(m_typeResolver->voidType()),
|
|
|
|
outputContent, QString());
|
|
|
|
m_body += u";\n"_s;
|
|
|
|
return;
|
|
|
|
}
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
2023-02-16 09:54:34 +00:00
|
|
|
|
2023-11-01 13:12:53 +00:00
|
|
|
if (isTrivial) {
|
|
|
|
// No actual conversion necessary. The 'as' is a no-op
|
|
|
|
m_body += conversion(inputContent, m_state.accumulatorOut(), input) + u";\n"_s;
|
|
|
|
return;
|
|
|
|
}
|
2023-02-16 09:54:34 +00:00
|
|
|
|
2023-11-01 13:12:53 +00:00
|
|
|
reject(u"unsupported type assertion"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_UNot()
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_UNot);
|
2022-03-21 09:21:18 +00:00
|
|
|
generateUnaryOperation(u"!"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_UPlus()
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_UPlus);
|
2022-03-21 09:21:18 +00:00
|
|
|
generateUnaryOperation(u"+"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_UMinus()
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_UMinus);
|
2022-03-21 09:21:18 +00:00
|
|
|
generateUnaryOperation(u"-"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_UCompl()
|
|
|
|
{
|
2022-07-29 14:27:57 +00:00
|
|
|
INJECT_TRACE_INFO(generate_UCompl);
|
|
|
|
generateUnaryOperation(u"~"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_Increment()
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_Increment);
|
2022-03-21 09:21:18 +00:00
|
|
|
generateInPlaceOperation(u"++"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_Decrement()
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_Decrement);
|
2022-03-21 09:21:18 +00:00
|
|
|
generateInPlaceOperation(u"--"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_Add(int lhs)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_Add);
|
2022-03-21 09:21:18 +00:00
|
|
|
generateArithmeticOperation(lhs, u"+"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_BitAnd(int lhs)
|
|
|
|
{
|
2022-07-29 14:27:57 +00:00
|
|
|
INJECT_TRACE_INFO(generate_BitAnd);
|
|
|
|
generateArithmeticOperation(lhs, u"&"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_BitOr(int lhs)
|
|
|
|
{
|
2022-07-29 14:27:57 +00:00
|
|
|
INJECT_TRACE_INFO(generate_BitOr);
|
|
|
|
generateArithmeticOperation(lhs, u"|"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_BitXor(int lhs)
|
|
|
|
{
|
2022-07-29 14:27:57 +00:00
|
|
|
INJECT_TRACE_INFO(generate_BitXor);
|
|
|
|
generateArithmeticOperation(lhs, u"^"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_UShr(int lhs)
|
|
|
|
{
|
2022-07-29 14:27:57 +00:00
|
|
|
INJECT_TRACE_INFO(generate_BitUShr);
|
|
|
|
generateShiftOperation(lhs, u">>"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_Shr(int lhs)
|
|
|
|
{
|
2022-07-29 14:27:57 +00:00
|
|
|
INJECT_TRACE_INFO(generate_Shr);
|
|
|
|
generateShiftOperation(lhs, u">>"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_Shl(int lhs)
|
|
|
|
{
|
2022-07-29 14:27:57 +00:00
|
|
|
INJECT_TRACE_INFO(generate_Shl);
|
|
|
|
generateShiftOperation(lhs, u"<<"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_BitAndConst(int rhs)
|
|
|
|
{
|
2022-07-29 14:27:57 +00:00
|
|
|
INJECT_TRACE_INFO(generate_BitAndConst);
|
|
|
|
generateArithmeticConstOperation(rhs, u"&"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_BitOrConst(int rhs)
|
|
|
|
{
|
2022-07-29 14:27:57 +00:00
|
|
|
INJECT_TRACE_INFO(generate_BitOrConst);
|
|
|
|
generateArithmeticConstOperation(rhs, u"|"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_BitXorConst(int rhs)
|
|
|
|
{
|
2022-07-29 14:27:57 +00:00
|
|
|
INJECT_TRACE_INFO(generate_BitXorConst);
|
|
|
|
generateArithmeticConstOperation(rhs, u"^"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_UShrConst(int rhs)
|
|
|
|
{
|
2022-07-29 14:27:57 +00:00
|
|
|
INJECT_TRACE_INFO(generate_UShrConst);
|
|
|
|
generateArithmeticConstOperation(rhs & 0x1f, u">>"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2022-07-29 14:27:57 +00:00
|
|
|
void QQmlJSCodeGenerator::generate_ShrConst(int rhs)
|
2021-11-16 15:49:49 +00:00
|
|
|
{
|
2022-07-29 14:27:57 +00:00
|
|
|
INJECT_TRACE_INFO(generate_ShrConst);
|
|
|
|
generateArithmeticConstOperation(rhs & 0x1f, u">>"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2022-07-29 14:27:57 +00:00
|
|
|
void QQmlJSCodeGenerator::generate_ShlConst(int rhs)
|
2021-11-16 15:49:49 +00:00
|
|
|
{
|
2022-07-29 14:27:57 +00:00
|
|
|
INJECT_TRACE_INFO(generate_ShlConst);
|
|
|
|
generateArithmeticConstOperation(rhs & 0x1f, u"<<"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_Exp(int lhs)
|
|
|
|
{
|
2022-07-29 14:27:57 +00:00
|
|
|
INJECT_TRACE_INFO(generate_Exp);
|
|
|
|
|
|
|
|
const QString lhsString = conversion(
|
2022-11-28 15:07:24 +00:00
|
|
|
registerType(lhs), m_state.readRegister(lhs), consumedRegisterVariable(lhs));
|
2022-07-29 14:27:57 +00:00
|
|
|
const QString rhsString = conversion(
|
2022-11-28 15:07:24 +00:00
|
|
|
m_state.accumulatorIn(), m_state.readAccumulator(),
|
|
|
|
consumedAccumulatorVariableIn());
|
2022-07-29 14:27:57 +00:00
|
|
|
|
|
|
|
Q_ASSERT(m_error->isValid() || !lhsString.isEmpty());
|
|
|
|
Q_ASSERT(m_error->isValid() || !rhsString.isEmpty());
|
|
|
|
|
|
|
|
const QQmlJSRegisterContent originalOut = m_typeResolver->original(m_state.accumulatorOut());
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s;
|
|
|
|
m_body += conversion(
|
|
|
|
originalOut, m_state.accumulatorOut(),
|
|
|
|
u"QQmlPrivate::jsExponentiate("_s + lhsString + u", "_s + rhsString + u')');
|
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_Mul(int lhs)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_Mul);
|
2022-03-21 09:21:18 +00:00
|
|
|
generateArithmeticOperation(lhs, u"*"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_Div(int lhs)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_Div);
|
2022-03-21 09:21:18 +00:00
|
|
|
generateArithmeticOperation(lhs, u"/"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_Mod(int lhs)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_Mod);
|
|
|
|
|
2023-02-21 10:47:08 +00:00
|
|
|
const auto lhsVar = convertStored(
|
2021-11-16 15:49:49 +00:00
|
|
|
registerType(lhs).storedType(), m_typeResolver->jsPrimitiveType(),
|
2022-11-28 15:07:24 +00:00
|
|
|
consumedRegisterVariable(lhs));
|
2023-02-21 10:47:08 +00:00
|
|
|
const auto rhsVar = convertStored(
|
2022-02-02 18:08:29 +00:00
|
|
|
m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
|
2022-11-28 15:07:24 +00:00
|
|
|
consumedAccumulatorVariableIn());
|
2022-06-03 13:12:10 +00:00
|
|
|
Q_ASSERT(m_error->isValid() || !lhsVar.isEmpty());
|
|
|
|
Q_ASSERT(m_error->isValid() || !rhsVar.isEmpty());
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut;
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u" = "_s;
|
2023-02-21 10:47:08 +00:00
|
|
|
m_body += conversion(m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut(),
|
2022-03-21 09:21:18 +00:00
|
|
|
u'(' + lhsVar + u" % "_s + rhsVar + u')');
|
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_Sub(int lhs)
|
|
|
|
{
|
|
|
|
INJECT_TRACE_INFO(generate_Sub);
|
2022-03-21 09:21:18 +00:00
|
|
|
generateArithmeticOperation(lhs, u"-"_s);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
|
|
|
|
{
|
|
|
|
Q_UNUSED(firstReg)
|
|
|
|
Q_UNUSED(count)
|
2022-11-10 13:50:18 +00:00
|
|
|
// Ignore. We reject uninitialized values anyway.
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_ThrowOnNullOrUndefined()
|
|
|
|
{
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generate_GetTemplateObject(int index)
|
|
|
|
{
|
|
|
|
Q_UNUSED(index)
|
|
|
|
BYTECODE_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
2022-01-17 10:40:44 +00:00
|
|
|
QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
|
|
|
|
QV4::Moth::Instr::Type type)
|
2021-11-16 15:49:49 +00:00
|
|
|
{
|
|
|
|
m_state.State::operator=(nextStateFromAnnotations(m_state, *m_annotations));
|
2022-02-02 18:08:29 +00:00
|
|
|
const auto accumulatorIn = m_state.registers.find(Accumulator);
|
2022-02-16 13:01:52 +00:00
|
|
|
if (accumulatorIn != m_state.registers.end()
|
2022-11-28 15:07:24 +00:00
|
|
|
&& isTypeStorable(m_typeResolver, accumulatorIn.value().content.storedType())) {
|
2023-08-14 10:37:42 +00:00
|
|
|
const QQmlJSRegisterContent &content = accumulatorIn.value().content;
|
2023-07-11 11:42:47 +00:00
|
|
|
m_state.accumulatorVariableIn = m_registerVariables.value(RegisterVariablesKey {
|
2023-08-14 10:37:42 +00:00
|
|
|
content.storedType()->internalName(),
|
|
|
|
Accumulator,
|
|
|
|
content.resultLookupIndex()
|
2023-07-11 11:42:47 +00:00
|
|
|
}).variableName;
|
2022-02-02 18:08:29 +00:00
|
|
|
Q_ASSERT(!m_state.accumulatorVariableIn.isEmpty());
|
|
|
|
} else {
|
|
|
|
m_state.accumulatorVariableIn.clear();
|
|
|
|
}
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
auto labelIt = m_labels.constFind(currentInstructionOffset());
|
|
|
|
if (labelIt != m_labels.constEnd()) {
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += *labelIt + u":;\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
m_skipUntilNextLabel = false;
|
2022-01-17 10:40:44 +00:00
|
|
|
} else if (m_skipUntilNextLabel && !instructionManipulatesContext(type)) {
|
2021-11-16 15:49:49 +00:00
|
|
|
return SkipInstruction;
|
|
|
|
}
|
|
|
|
|
2022-02-02 18:08:29 +00:00
|
|
|
if (m_state.changedRegisterIndex() == Accumulator)
|
|
|
|
m_state.accumulatorVariableOut = changedRegisterVariable();
|
|
|
|
else
|
|
|
|
m_state.accumulatorVariableOut.clear();
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
// If the accumulator type is valid, we want an accumulator variable.
|
|
|
|
// If not, we don't want one.
|
2022-02-02 18:08:29 +00:00
|
|
|
Q_ASSERT(m_state.changedRegisterIndex() == Accumulator
|
|
|
|
|| m_state.accumulatorVariableOut.isEmpty());
|
|
|
|
Q_ASSERT(m_state.changedRegisterIndex() != Accumulator
|
2022-02-16 13:01:52 +00:00
|
|
|
|| !m_state.accumulatorVariableOut.isEmpty()
|
|
|
|
|| !isTypeStorable(m_typeResolver, m_state.changedRegister().storedType()));
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-02-16 13:01:52 +00:00
|
|
|
// If the instruction has no side effects and doesn't write any register, it's dead.
|
|
|
|
// We might still need the label, though, and the source code comment.
|
2023-10-24 14:30:28 +00:00
|
|
|
if (!m_state.hasSideEffects() && changedRegisterVariable().isEmpty()) {
|
|
|
|
generateJumpCodeWithTypeConversions(0);
|
2022-02-16 13:01:52 +00:00
|
|
|
return SkipInstruction;
|
2023-10-24 14:30:28 +00:00
|
|
|
}
|
2022-02-16 13:01:52 +00:00
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
return ProcessInstruction;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::endInstruction(QV4::Moth::Instr::Type)
|
|
|
|
{
|
2022-06-27 14:26:43 +00:00
|
|
|
if (!m_skipUntilNextLabel)
|
|
|
|
generateJumpCodeWithTypeConversions(0);
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generateSetInstructionPointer()
|
|
|
|
{
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"aotContext->setInstructionPointer("_s
|
|
|
|
+ QString::number(nextInstructionOffset()) + u");\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generateExceptionCheck()
|
|
|
|
{
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"if (aotContext->engine->hasError())\n"_s;
|
|
|
|
m_body += u" return "_s + errorReturnValue() + u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2023-08-25 10:04:18 +00:00
|
|
|
void QQmlJSCodeGenerator::generateEqualityOperation(
|
2023-11-03 15:02:27 +00:00
|
|
|
const QQmlJSRegisterContent &lhsContent, const QQmlJSRegisterContent &rhsContent,
|
|
|
|
const QString &lhsName, const QString &rhsName, const QString &function, bool invert)
|
2021-11-16 15:49:49 +00:00
|
|
|
{
|
2023-11-02 15:27:01 +00:00
|
|
|
const bool lhsIsOptional = m_typeResolver->isOptionalType(lhsContent);
|
|
|
|
const bool rhsIsOptional = m_typeResolver->isOptionalType(rhsContent);
|
|
|
|
|
|
|
|
const auto rhsContained = rhsIsOptional
|
|
|
|
? m_typeResolver->extractNonVoidFromOptionalType(rhsContent)
|
|
|
|
: m_typeResolver->containedType(rhsContent);
|
|
|
|
|
|
|
|
const auto lhsContained = lhsIsOptional
|
|
|
|
? m_typeResolver->extractNonVoidFromOptionalType(lhsContent)
|
|
|
|
: m_typeResolver->containedType(lhsContent);
|
|
|
|
|
|
|
|
const bool isStrict = function == "strictlyEquals"_L1;
|
|
|
|
const bool strictlyComparableWithVar
|
|
|
|
= isStrict && canStrictlyCompareWithVar(m_typeResolver, lhsContained, rhsContained);
|
2021-11-16 15:49:49 +00:00
|
|
|
auto isComparable = [&]() {
|
2023-11-02 15:27:01 +00:00
|
|
|
if (m_typeResolver->isPrimitive(lhsContent) && m_typeResolver->isPrimitive(rhsContent))
|
2021-11-16 15:49:49 +00:00
|
|
|
return true;
|
2023-11-02 15:27:01 +00:00
|
|
|
if (m_typeResolver->isNumeric(lhsContent) && rhsContent.isEnumeration())
|
2021-11-16 15:49:49 +00:00
|
|
|
return true;
|
2023-11-02 15:27:01 +00:00
|
|
|
if (m_typeResolver->isNumeric(rhsContent) && lhsContent.isEnumeration())
|
2021-11-16 15:49:49 +00:00
|
|
|
return true;
|
2023-01-30 21:33:00 +00:00
|
|
|
if (strictlyComparableWithVar)
|
2022-12-13 00:43:09 +00:00
|
|
|
return true;
|
2023-11-02 15:27:01 +00:00
|
|
|
if (canCompareWithQObject(m_typeResolver, lhsContained, rhsContained))
|
2023-01-06 15:39:43 +00:00
|
|
|
return true;
|
2023-11-02 15:27:01 +00:00
|
|
|
if (canCompareWithQUrl(m_typeResolver, lhsContained, rhsContained))
|
2023-02-15 12:25:25 +00:00
|
|
|
return true;
|
2021-11-16 15:49:49 +00:00
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2023-11-03 15:02:27 +00:00
|
|
|
const auto retrieveOriginal = [this](const QQmlJSRegisterContent &content) {
|
|
|
|
const auto contained = m_typeResolver->containedType(content);
|
|
|
|
const auto original = m_typeResolver->original(content);
|
|
|
|
const auto containedOriginal = m_typeResolver->containedType(original);
|
|
|
|
|
2023-12-05 12:03:34 +00:00
|
|
|
if (m_typeResolver->equals(
|
|
|
|
m_typeResolver->genericType(containedOriginal), original.storedType())) {
|
|
|
|
// The original type doesn't need any wrapping.
|
|
|
|
return original;
|
|
|
|
} else if (m_typeResolver->equals(contained, containedOriginal)) {
|
2023-11-03 15:02:27 +00:00
|
|
|
if (original.isConversion()) {
|
|
|
|
// The original conversion origins are more accurate
|
|
|
|
return original.storedIn(content.storedType());
|
|
|
|
}
|
|
|
|
} else if (m_typeResolver->canHold(contained, containedOriginal)) {
|
|
|
|
return original.storedIn(content.storedType());
|
|
|
|
}
|
|
|
|
|
|
|
|
return content;
|
|
|
|
};
|
|
|
|
|
2022-02-16 13:01:52 +00:00
|
|
|
if (!isComparable()) {
|
2023-11-03 15:02:27 +00:00
|
|
|
QQmlJSRegisterContent lhsOriginal = retrieveOriginal(lhsContent);
|
|
|
|
QQmlJSRegisterContent rhsOriginal = retrieveOriginal(rhsContent);
|
|
|
|
if (lhsOriginal != lhsContent || rhsOriginal != rhsContent) {
|
|
|
|
// If either side is simply a wrapping of a specific type into a more general one, we
|
|
|
|
// can compare the original types instead. You can't nest wrappings after all.
|
2024-02-07 09:11:41 +00:00
|
|
|
generateEqualityOperation(lhsOriginal, rhsOriginal,
|
|
|
|
conversion(lhsContent.storedType(), lhsOriginal, lhsName),
|
|
|
|
conversion(rhsContent.storedType(), rhsOriginal, rhsName),
|
|
|
|
function, invert);
|
2023-11-03 15:02:27 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-11-02 15:27:01 +00:00
|
|
|
reject(u"incomparable types %1 and %2"_s.arg(
|
|
|
|
rhsContent.descriptiveName(), lhsContent.descriptiveName()));
|
2022-02-16 13:01:52 +00:00
|
|
|
}
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
const QQmlJSScope::ConstPtr lhsType = lhsContent.storedType();
|
2023-11-02 15:27:01 +00:00
|
|
|
const QQmlJSScope::ConstPtr rhsType = rhsContent.storedType();
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2023-08-25 10:04:18 +00:00
|
|
|
if (strictlyComparableWithVar) {
|
2022-12-13 00:43:09 +00:00
|
|
|
// Determine which side is holding a storable type
|
2023-11-02 15:27:01 +00:00
|
|
|
if (!lhsName.isEmpty() && rhsName.isEmpty()) {
|
|
|
|
// lhs register holds var type and rhs is not storable
|
|
|
|
generateVariantEqualityComparison(rhsContent, lhsName, invert);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!rhsName.isEmpty() && lhsName.isEmpty()) {
|
2022-12-13 00:43:09 +00:00
|
|
|
// lhs content is not storable and rhs is var type
|
2023-11-02 15:27:01 +00:00
|
|
|
generateVariantEqualityComparison(lhsContent, rhsName, invert);
|
|
|
|
return;
|
2022-12-13 00:43:09 +00:00
|
|
|
}
|
2023-08-25 10:04:18 +00:00
|
|
|
|
2023-12-05 12:03:34 +00:00
|
|
|
if (m_typeResolver->registerContains(lhsContent, m_typeResolver->varType())) {
|
|
|
|
generateVariantEqualityComparison(rhsContent, rhsName, lhsName, invert);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_typeResolver->registerContains(rhsContent, m_typeResolver->varType())) {
|
|
|
|
generateVariantEqualityComparison(lhsContent, lhsName, rhsName, invert);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-11-02 15:27:01 +00:00
|
|
|
// It shouldn't be possible to get here because optional null should be stored in
|
|
|
|
// QJSPrimitiveValue, not in QVariant. But let's rather be safe than sorry.
|
|
|
|
reject(u"comparison of optional null"_s);
|
|
|
|
}
|
2023-08-25 10:04:18 +00:00
|
|
|
|
|
|
|
const auto comparison = [&]() -> QString {
|
2023-11-02 15:27:01 +00:00
|
|
|
const auto primitive = m_typeResolver->jsPrimitiveType();
|
2023-08-25 10:04:18 +00:00
|
|
|
const QString sign = invert ? u" != "_s : u" == "_s;
|
|
|
|
|
|
|
|
if (m_typeResolver->equals(lhsType, rhsType)
|
2023-11-02 15:27:01 +00:00
|
|
|
&& !m_typeResolver->equals(lhsType, primitive)
|
|
|
|
&& !m_typeResolver->equals(lhsType, m_typeResolver->varType())) {
|
2023-08-25 10:04:18 +00:00
|
|
|
|
|
|
|
// Straight forward comparison of equal types,
|
|
|
|
// except QJSPrimitiveValue which has two comparison functions.
|
|
|
|
|
|
|
|
if (isTypeStorable(m_typeResolver, lhsType))
|
2023-11-02 15:27:01 +00:00
|
|
|
return lhsName + sign + rhsName;
|
2023-08-25 10:04:18 +00:00
|
|
|
|
|
|
|
// null === null and undefined === undefined
|
|
|
|
return invert ? u"false"_s : u"true"_s;
|
|
|
|
}
|
|
|
|
|
2023-11-02 15:27:01 +00:00
|
|
|
if (canCompareWithQObject(m_typeResolver, lhsType, rhsType)) {
|
2023-08-25 10:04:18 +00:00
|
|
|
// Comparison of QObject-derived with nullptr or different QObject-derived.
|
2023-11-02 15:27:01 +00:00
|
|
|
return (isTypeStorable(m_typeResolver, lhsType) ? lhsName : u"nullptr"_s)
|
2023-08-25 10:04:18 +00:00
|
|
|
+ sign
|
2023-11-02 15:27:01 +00:00
|
|
|
+ (isTypeStorable(m_typeResolver, rhsType) ? rhsName : u"nullptr"_s);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (canCompareWithQObject(m_typeResolver, lhsContained, rhsContained)) {
|
|
|
|
// Comparison of optional QObject-derived with nullptr or different QObject-derived.
|
|
|
|
// Mind that null == undefined but null !== undefined
|
|
|
|
// Therefore the isStrict dance.
|
|
|
|
|
|
|
|
QString result;
|
|
|
|
if (isStrict) {
|
|
|
|
if (lhsIsOptional) {
|
|
|
|
if (rhsIsOptional) {
|
|
|
|
// If both are invalid we're fine
|
|
|
|
result += u"(!"_s
|
|
|
|
+ lhsName + u".isValid() && !"_s
|
|
|
|
+ rhsName + u".isValid()) || "_s;
|
|
|
|
}
|
|
|
|
|
|
|
|
result += u'(' + lhsName + u".isValid() && "_s;
|
|
|
|
} else {
|
|
|
|
result += u'(';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rhsIsOptional) {
|
|
|
|
result += rhsName + u".isValid() && "_s;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
result += u'(';
|
|
|
|
}
|
|
|
|
|
|
|
|
// We do not implement comparison with explicit undefined, yet. Only with null.
|
|
|
|
Q_ASSERT(!m_typeResolver->equals(lhsType, m_typeResolver->voidType()));
|
|
|
|
Q_ASSERT(!m_typeResolver->equals(rhsType, m_typeResolver->voidType()));
|
|
|
|
|
|
|
|
const auto resolvedName = [&](const QString name) -> QString {
|
|
|
|
// If isStrict we check validity already before.
|
|
|
|
const QString content = u"*static_cast<QObject **>("_s + name + u".data())"_s;
|
|
|
|
return isStrict
|
|
|
|
? content
|
|
|
|
: u'(' + name + u".isValid() ? "_s + content + u" : nullptr)"_s;
|
|
|
|
};
|
|
|
|
|
|
|
|
const QString lhsResolved = lhsIsOptional ? resolvedName(lhsName) : lhsName;
|
|
|
|
const QString rhsResolved = rhsIsOptional ? resolvedName(rhsName) : rhsName;
|
|
|
|
|
|
|
|
return (invert ? u"!("_s : u"("_s) + result
|
|
|
|
+ (isTypeStorable(m_typeResolver, lhsType) ? lhsResolved : u"nullptr"_s)
|
|
|
|
+ u" == "_s
|
|
|
|
+ (isTypeStorable(m_typeResolver, rhsType) ? rhsResolved : u"nullptr"_s)
|
|
|
|
+ u"))"_s;
|
2023-08-25 10:04:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((m_typeResolver->isUnsignedInteger(rhsType)
|
|
|
|
&& m_typeResolver->isUnsignedInteger(lhsType))
|
|
|
|
|| (m_typeResolver->isSignedInteger(rhsType)
|
|
|
|
&& m_typeResolver->isSignedInteger(lhsType))) {
|
|
|
|
// Both integers of same signedness: Let the C++ compiler perform the type promotion
|
2023-11-02 15:27:01 +00:00
|
|
|
return lhsName + sign + rhsName;
|
2023-08-25 10:04:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m_typeResolver->equals(rhsType, m_typeResolver->boolType())
|
|
|
|
&& m_typeResolver->isIntegral(lhsType)) {
|
|
|
|
// Integral and bool: We can promote the bool to the integral type
|
2023-11-02 15:27:01 +00:00
|
|
|
return lhsName + sign + convertStored(rhsType, lhsType, rhsName);
|
2023-08-25 10:04:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m_typeResolver->equals(lhsType, m_typeResolver->boolType())
|
|
|
|
&& m_typeResolver->isIntegral(rhsType)) {
|
|
|
|
// Integral and bool: We can promote the bool to the integral type
|
2023-11-02 15:27:01 +00:00
|
|
|
return convertStored(lhsType, rhsType, lhsName) + sign + rhsName;
|
2023-08-25 10:04:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(rhsType)) {
|
|
|
|
// Both numbers: promote them to double
|
|
|
|
return convertStored(lhsType, m_typeResolver->realType(), lhsName)
|
|
|
|
+ sign
|
2023-11-02 15:27:01 +00:00
|
|
|
+ convertStored(rhsType, m_typeResolver->realType(), rhsName);
|
2023-08-25 10:04:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If none of the above matches, we have to use QJSPrimitiveValue
|
|
|
|
return (invert ? u"!"_s : QString())
|
|
|
|
+ convertStored(lhsType, primitive, lhsName)
|
2023-11-02 15:27:01 +00:00
|
|
|
+ u'.' + function + u'(' + convertStored(rhsType, primitive, rhsName) + u')';
|
2023-08-25 10:04:18 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s;
|
|
|
|
m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut(), comparison());
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generateCompareOperation(int lhs, const QString &cppOperator)
|
|
|
|
{
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
const auto lhsType = registerType(lhs);
|
|
|
|
const QQmlJSScope::ConstPtr compareType =
|
2022-02-02 18:08:29 +00:00
|
|
|
m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(m_state.accumulatorIn())
|
|
|
|
? m_typeResolver->merge(lhsType, m_state.accumulatorIn()).storedType()
|
2021-11-16 15:49:49 +00:00
|
|
|
: m_typeResolver->jsPrimitiveType();
|
2022-02-16 15:34:56 +00:00
|
|
|
|
|
|
|
m_body += conversion(
|
2023-02-21 10:47:08 +00:00
|
|
|
m_typeResolver->boolType(), m_state.accumulatorOut(),
|
|
|
|
convertStored(registerType(lhs).storedType(), compareType,
|
|
|
|
consumedRegisterVariable(lhs))
|
2022-02-16 15:34:56 +00:00
|
|
|
+ u' ' + cppOperator + u' '
|
2023-02-21 10:47:08 +00:00
|
|
|
+ convertStored(m_state.accumulatorIn().storedType(), compareType,
|
|
|
|
consumedAccumulatorVariableIn()));
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generateArithmeticOperation(int lhs, const QString &cppOperator)
|
|
|
|
{
|
2022-07-29 14:27:57 +00:00
|
|
|
generateArithmeticOperation(
|
2022-11-28 15:07:24 +00:00
|
|
|
conversion(registerType(lhs), m_state.readRegister(lhs),
|
|
|
|
consumedRegisterVariable(lhs)),
|
2022-07-29 14:27:57 +00:00
|
|
|
conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
|
2022-11-28 15:07:24 +00:00
|
|
|
consumedAccumulatorVariableIn()),
|
2022-07-29 14:27:57 +00:00
|
|
|
cppOperator);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generateShiftOperation(int lhs, const QString &cppOperator)
|
|
|
|
{
|
|
|
|
generateArithmeticOperation(
|
2022-11-28 15:07:24 +00:00
|
|
|
conversion(registerType(lhs), m_state.readRegister(lhs),
|
|
|
|
consumedRegisterVariable(lhs)),
|
2022-07-29 14:27:57 +00:00
|
|
|
u'(' + conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
|
2022-11-28 15:07:24 +00:00
|
|
|
consumedAccumulatorVariableIn()) + u" & 0x1f)"_s,
|
2022-07-29 14:27:57 +00:00
|
|
|
cppOperator);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generateArithmeticOperation(
|
|
|
|
const QString &lhs, const QString &rhs, const QString &cppOperator)
|
|
|
|
{
|
|
|
|
Q_ASSERT(m_error->isValid() || !lhs.isEmpty());
|
|
|
|
Q_ASSERT(m_error->isValid() || !rhs.isEmpty());
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-02-16 15:34:56 +00:00
|
|
|
const QQmlJSRegisterContent originalOut = m_typeResolver->original(m_state.accumulatorOut());
|
2021-11-16 15:49:49 +00:00
|
|
|
m_body += m_state.accumulatorVariableOut;
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u" = "_s;
|
2022-02-16 13:01:52 +00:00
|
|
|
m_body += conversion(
|
2022-02-16 15:34:56 +00:00
|
|
|
originalOut, m_state.accumulatorOut(),
|
2022-07-29 14:27:57 +00:00
|
|
|
u'(' + lhs + u' ' + cppOperator + u' ' + rhs + u')');
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2022-07-29 14:27:57 +00:00
|
|
|
void QQmlJSCodeGenerator::generateArithmeticConstOperation(int rhsConst, const QString &cppOperator)
|
|
|
|
{
|
|
|
|
generateArithmeticOperation(
|
|
|
|
conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
|
2022-11-28 15:07:24 +00:00
|
|
|
consumedAccumulatorVariableIn()),
|
2023-03-27 16:00:46 +00:00
|
|
|
conversion(m_typeResolver->globalType(m_typeResolver->int32Type()),
|
2022-07-29 14:27:57 +00:00
|
|
|
m_state.readAccumulator(), QString::number(rhsConst)),
|
|
|
|
cppOperator);
|
|
|
|
}
|
|
|
|
|
2022-02-16 13:01:52 +00:00
|
|
|
void QQmlJSCodeGenerator::generateUnaryOperation(const QString &cppOperator)
|
|
|
|
{
|
2023-10-05 14:54:03 +00:00
|
|
|
const auto var = conversion(m_state.accumulatorIn(),
|
|
|
|
m_typeResolver->original(m_state.readAccumulator()),
|
2022-11-28 15:07:24 +00:00
|
|
|
consumedAccumulatorVariableIn());
|
2022-02-16 13:01:52 +00:00
|
|
|
|
|
|
|
if (var == m_state.accumulatorVariableOut) {
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s + cppOperator + var + u";\n"_s;
|
2022-02-16 13:01:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto original = m_typeResolver->original(m_state.accumulatorOut());
|
|
|
|
if (m_state.accumulatorOut() == original) {
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s + var + u";\n"_s;
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s
|
|
|
|
+ cppOperator + m_state.accumulatorVariableOut + u";\n"_s;
|
2022-02-16 13:01:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s + conversion(
|
2023-02-21 10:47:08 +00:00
|
|
|
original, m_state.accumulatorOut(), cppOperator + var) + u";\n"_s;
|
2022-02-16 13:01:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QQmlJSCodeGenerator::generateInPlaceOperation(const QString &cppOperator)
|
|
|
|
{
|
2022-11-28 15:07:24 +00:00
|
|
|
{
|
|
|
|
// If actually in place, we cannot consume the variable.
|
|
|
|
const QString var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
|
|
|
|
m_state.accumulatorVariableIn);
|
|
|
|
if (var == m_state.accumulatorVariableOut) {
|
|
|
|
m_body += cppOperator + var + u";\n"_s;
|
|
|
|
return;
|
|
|
|
}
|
2022-02-16 13:01:52 +00:00
|
|
|
}
|
|
|
|
|
2022-11-28 15:07:24 +00:00
|
|
|
const QString var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
|
|
|
|
consumedAccumulatorVariableIn());
|
|
|
|
|
2022-02-16 13:01:52 +00:00
|
|
|
const auto original = m_typeResolver->original(m_state.accumulatorOut());
|
|
|
|
if (m_state.accumulatorOut() == original) {
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s + var + u";\n"_s;
|
|
|
|
m_body += cppOperator + m_state.accumulatorVariableOut + u";\n"_s;
|
2022-02-16 13:01:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"{\n"_s;
|
|
|
|
m_body += u"auto converted = "_s + var + u";\n"_s;
|
|
|
|
m_body += m_state.accumulatorVariableOut + u" = "_s + conversion(
|
2023-02-21 10:47:08 +00:00
|
|
|
original, m_state.accumulatorOut(), u'('
|
|
|
|
+ cppOperator + u"converted)"_s) + u";\n"_s;
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"}\n"_s;
|
2022-02-16 13:01:52 +00:00
|
|
|
}
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &initialization,
|
|
|
|
const QString &resultPreparation)
|
|
|
|
{
|
2024-02-05 17:10:50 +00:00
|
|
|
m_body += u"#ifndef QT_NO_DEBUG\n"_s;
|
|
|
|
generateSetInstructionPointer();
|
|
|
|
m_body += u"#endif\n"_s;
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
if (!resultPreparation.isEmpty())
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += resultPreparation + u";\n"_s;
|
|
|
|
m_body += u"while (!"_s + lookup + u") {\n"_s;
|
2024-02-05 17:10:50 +00:00
|
|
|
|
|
|
|
m_body += u"#ifdef QT_NO_DEBUG\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
generateSetInstructionPointer();
|
2024-02-05 17:10:50 +00:00
|
|
|
m_body += u"#endif\n"_s;
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += initialization + u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
generateExceptionCheck();
|
|
|
|
if (!resultPreparation.isEmpty())
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += resultPreparation + u";\n"_s;
|
|
|
|
m_body += u"}\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2022-02-17 12:04:35 +00:00
|
|
|
void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(int relativeOffset)
|
2021-11-16 15:49:49 +00:00
|
|
|
{
|
2022-03-28 07:21:21 +00:00
|
|
|
QString conversionCode;
|
|
|
|
const int absoluteOffset = nextInstructionOffset() + relativeOffset;
|
2022-02-16 14:12:26 +00:00
|
|
|
const auto annotation = m_annotations->find(absoluteOffset);
|
2021-11-16 15:49:49 +00:00
|
|
|
if (annotation != m_annotations->constEnd()) {
|
2022-02-16 14:12:26 +00:00
|
|
|
const auto &conversions = annotation->second.typeConversions;
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
for (auto regIt = conversions.constBegin(), regEnd = conversions.constEnd();
|
|
|
|
regIt != regEnd; ++regIt) {
|
2022-11-28 15:07:24 +00:00
|
|
|
const QQmlJSRegisterContent targetType = regIt.value().content;
|
2023-11-22 09:33:59 +00:00
|
|
|
if (!targetType.isValid() || !isTypeStorable(m_typeResolver, targetType.storedType()))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
const int registerIndex = regIt.key();
|
|
|
|
const auto variable = m_registerVariables.constFind(RegisterVariablesKey {
|
|
|
|
targetType.storedType()->internalName(),
|
|
|
|
registerIndex,
|
|
|
|
targetType.resultLookupIndex()
|
|
|
|
});
|
|
|
|
|
|
|
|
if (variable == m_registerVariables.constEnd())
|
2021-11-16 15:49:49 +00:00
|
|
|
continue;
|
2022-02-02 18:08:29 +00:00
|
|
|
|
|
|
|
QQmlJSRegisterContent currentType;
|
|
|
|
QString currentVariable;
|
|
|
|
if (registerIndex == m_state.changedRegisterIndex()) {
|
|
|
|
currentVariable = changedRegisterVariable();
|
2023-11-22 09:33:59 +00:00
|
|
|
if (variable->variableName == currentVariable)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
currentType = m_state.changedRegister();
|
|
|
|
currentVariable = u"std::move("_s + currentVariable + u')';
|
2022-02-02 18:08:29 +00:00
|
|
|
} else {
|
2023-11-22 09:33:59 +00:00
|
|
|
const auto it = m_state.registers.find(registerIndex);
|
|
|
|
if (it == m_state.registers.end()
|
|
|
|
|| variable->variableName == registerVariable(registerIndex)) {
|
2022-02-02 18:08:29 +00:00
|
|
|
continue;
|
2023-11-22 09:33:59 +00:00
|
|
|
}
|
|
|
|
|
2022-11-28 15:07:24 +00:00
|
|
|
currentType = it.value().content;
|
|
|
|
currentVariable = consumedRegisterVariable(registerIndex);
|
2022-02-02 18:08:29 +00:00
|
|
|
}
|
|
|
|
|
2022-02-04 21:35:23 +00:00
|
|
|
// Actually == here. We want the jump code also for equal types
|
2023-11-22 09:33:59 +00:00
|
|
|
if (currentType == targetType)
|
2021-11-16 15:49:49 +00:00
|
|
|
continue;
|
|
|
|
|
2023-07-11 11:42:47 +00:00
|
|
|
conversionCode += variable->variableName;
|
2022-03-21 09:21:18 +00:00
|
|
|
conversionCode += u" = "_s;
|
2022-03-28 07:21:21 +00:00
|
|
|
conversionCode += conversion(currentType, targetType, currentVariable);
|
2022-03-21 09:21:18 +00:00
|
|
|
conversionCode += u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (relativeOffset) {
|
|
|
|
auto labelIt = m_labels.find(absoluteOffset);
|
|
|
|
if (labelIt == m_labels.end())
|
2022-10-05 05:29:16 +00:00
|
|
|
labelIt = m_labels.insert(absoluteOffset, u"label_%1"_s.arg(m_labels.size()));
|
2022-03-21 09:21:18 +00:00
|
|
|
conversionCode += u" goto "_s + *labelIt + u";\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
2022-03-28 07:21:21 +00:00
|
|
|
|
|
|
|
if (!conversionCode.isEmpty())
|
2022-03-21 09:21:18 +00:00
|
|
|
m_body += u"{\n"_s + conversionCode + u"}\n"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QString QQmlJSCodeGenerator::registerVariable(int index) const
|
|
|
|
{
|
2023-08-14 10:37:42 +00:00
|
|
|
const QQmlJSRegisterContent &content = registerType(index);
|
2023-07-11 11:42:47 +00:00
|
|
|
const auto it = m_registerVariables.constFind(RegisterVariablesKey {
|
2023-08-14 10:37:42 +00:00
|
|
|
content.storedType()->internalName(),
|
|
|
|
index,
|
|
|
|
content.resultLookupIndex()
|
2023-07-11 11:42:47 +00:00
|
|
|
});
|
|
|
|
if (it != m_registerVariables.constEnd())
|
|
|
|
return it->variableName;
|
2022-06-20 12:44:35 +00:00
|
|
|
|
|
|
|
return QString();
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2023-08-14 10:37:42 +00:00
|
|
|
QString QQmlJSCodeGenerator::lookupVariable(int lookupIndex) const
|
|
|
|
{
|
|
|
|
for (auto it = m_registerVariables.constBegin(), end = m_registerVariables.constEnd(); it != end; ++it) {
|
|
|
|
if (it.key().lookupIndex == lookupIndex)
|
|
|
|
return it->variableName;
|
|
|
|
}
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
2022-11-28 15:07:24 +00:00
|
|
|
QString QQmlJSCodeGenerator::consumedRegisterVariable(int index) const
|
|
|
|
{
|
|
|
|
const QString var = registerVariable(index);
|
2023-06-28 09:29:46 +00:00
|
|
|
if (var.isEmpty() || !shouldMoveRegister(index))
|
2022-11-28 15:07:24 +00:00
|
|
|
return var;
|
|
|
|
return u"std::move(" + var + u")";
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QQmlJSCodeGenerator::consumedAccumulatorVariableIn() const
|
|
|
|
{
|
2023-06-28 09:29:46 +00:00
|
|
|
return shouldMoveRegister(Accumulator)
|
2022-11-28 15:07:24 +00:00
|
|
|
? u"std::move(" + m_state.accumulatorVariableIn + u")"
|
|
|
|
: m_state.accumulatorVariableIn;
|
|
|
|
}
|
|
|
|
|
2022-02-02 18:08:29 +00:00
|
|
|
QString QQmlJSCodeGenerator::changedRegisterVariable() const
|
|
|
|
{
|
2023-08-14 10:37:42 +00:00
|
|
|
const QQmlJSRegisterContent &changedRegister = m_state.changedRegister();
|
|
|
|
|
|
|
|
const QQmlJSScope::ConstPtr storedType = changedRegister.storedType();
|
2023-07-11 11:42:47 +00:00
|
|
|
if (storedType.isNull())
|
|
|
|
return QString();
|
|
|
|
|
|
|
|
return m_registerVariables.value(RegisterVariablesKey {
|
|
|
|
storedType->internalName(),
|
2023-08-14 10:37:42 +00:00
|
|
|
m_state.changedRegisterIndex(),
|
|
|
|
changedRegister.resultLookupIndex()
|
2023-07-11 11:42:47 +00:00
|
|
|
}).variableName;
|
2022-02-02 18:08:29 +00:00
|
|
|
}
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
QQmlJSRegisterContent QQmlJSCodeGenerator::registerType(int index) const
|
|
|
|
{
|
2022-06-20 12:44:35 +00:00
|
|
|
auto it = m_state.registers.find(index);
|
|
|
|
if (it != m_state.registers.end())
|
2022-11-28 15:07:24 +00:00
|
|
|
return it.value().content;
|
2022-06-20 12:44:35 +00:00
|
|
|
|
|
|
|
return QQmlJSRegisterContent();
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2023-08-14 10:37:42 +00:00
|
|
|
QQmlJSRegisterContent QQmlJSCodeGenerator::lookupType(int lookupIndex) const
|
|
|
|
{
|
|
|
|
auto it = m_state.lookups.find(lookupIndex);
|
|
|
|
if (it != m_state.lookups.end())
|
|
|
|
return it.value().content;
|
|
|
|
|
|
|
|
return QQmlJSRegisterContent();
|
|
|
|
}
|
|
|
|
|
2023-06-28 09:29:46 +00:00
|
|
|
bool QQmlJSCodeGenerator::shouldMoveRegister(int index) const
|
|
|
|
{
|
|
|
|
return m_state.canMoveReadRegister(index)
|
|
|
|
&& !m_typeResolver->isTriviallyCopyable(m_state.readRegister(index).storedType());
|
|
|
|
}
|
|
|
|
|
2023-03-14 13:51:36 +00:00
|
|
|
QString QQmlJSCodeGenerator::conversion(
|
|
|
|
const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to, const QString &variable)
|
|
|
|
{
|
|
|
|
const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(to);
|
|
|
|
|
2023-12-21 14:06:28 +00:00
|
|
|
// If from is QJSPrimitiveValue and to contains a primitive we coerce using QJSPrimitiveValue
|
2023-03-14 13:51:36 +00:00
|
|
|
if (m_typeResolver->registerIsStoredIn(from, m_typeResolver->jsPrimitiveType())
|
2023-12-21 14:06:28 +00:00
|
|
|
&& m_typeResolver->isPrimitive(to)) {
|
|
|
|
|
|
|
|
QString primitive = [&]() {
|
|
|
|
if (m_typeResolver->equals(contained, m_typeResolver->jsPrimitiveType()))
|
|
|
|
return variable;
|
|
|
|
|
|
|
|
const QString conversion = variable + u".to<QJSPrimitiveValue::%1>()"_s;
|
|
|
|
if (m_typeResolver->equals(contained, m_typeResolver->boolType()))
|
|
|
|
return conversion.arg(u"Boolean"_s);
|
|
|
|
if (m_typeResolver->isIntegral(to))
|
|
|
|
return conversion.arg(u"Integer"_s);
|
|
|
|
if (m_typeResolver->isNumeric(to))
|
|
|
|
return conversion.arg(u"Double"_s);
|
|
|
|
if (m_typeResolver->equals(contained, m_typeResolver->stringType()))
|
|
|
|
return conversion.arg(u"String"_s);
|
|
|
|
reject(u"Conversion of QJSPrimitiveValue to "_s + contained->internalName());
|
|
|
|
return QString();
|
|
|
|
}();
|
|
|
|
|
|
|
|
if (primitive.isEmpty())
|
|
|
|
return primitive;
|
|
|
|
|
|
|
|
return convertStored(m_typeResolver->jsPrimitiveType(), to.storedType(), primitive);
|
2023-03-14 13:51:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m_typeResolver->registerIsStoredIn(to, contained)
|
2023-05-02 09:08:13 +00:00
|
|
|
|| m_typeResolver->isNumeric(to.storedType())
|
2023-03-14 13:51:36 +00:00
|
|
|
|| to.storedType()->isReferenceType()
|
|
|
|
|| m_typeResolver->registerContains(from, contained)) {
|
|
|
|
// If:
|
|
|
|
// * the output is not actually wrapped at all, or
|
2023-05-02 09:08:13 +00:00
|
|
|
// * the output is stored in a numeric type (as there are no internals to a number), or
|
2023-03-14 13:51:36 +00:00
|
|
|
// * the output is a QObject pointer, or
|
|
|
|
// * we merely wrap the value into a new container,
|
|
|
|
// we can convert by stored type.
|
|
|
|
return convertStored(from.storedType(), to.storedType(), variable);
|
|
|
|
} else {
|
|
|
|
return convertContained(from, to, variable);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-21 10:47:08 +00:00
|
|
|
QString QQmlJSCodeGenerator::convertStored(
|
|
|
|
const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to, const QString &variable)
|
2021-11-16 15:49:49 +00:00
|
|
|
{
|
|
|
|
// TODO: most values can be moved, which is much more efficient with the common types.
|
|
|
|
// add a move(from, to, variable) function that implements the moves.
|
|
|
|
Q_ASSERT(!to->isComposite()); // We cannot directly convert to composites.
|
|
|
|
|
2022-02-03 16:31:33 +00:00
|
|
|
const auto jsValueType = m_typeResolver->jsValueType();
|
|
|
|
const auto varType = m_typeResolver->varType();
|
|
|
|
const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType();
|
|
|
|
const auto boolType = m_typeResolver->boolType();
|
|
|
|
|
2023-02-15 12:04:03 +00:00
|
|
|
auto zeroBoolOrInt = [&](const QQmlJSScope::ConstPtr &to) {
|
2022-02-16 12:33:47 +00:00
|
|
|
if (m_typeResolver->equals(to, boolType))
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"false"_s;
|
2023-03-27 16:00:46 +00:00
|
|
|
if (m_typeResolver->isSignedInteger(to))
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"0"_s;
|
2023-03-27 16:00:46 +00:00
|
|
|
if (m_typeResolver->isUnsignedInteger(to))
|
2022-07-29 14:25:22 +00:00
|
|
|
return u"0u"_s;
|
2022-02-16 12:33:47 +00:00
|
|
|
return QString();
|
|
|
|
};
|
|
|
|
|
|
|
|
if (m_typeResolver->equals(from, m_typeResolver->voidType())) {
|
|
|
|
if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
|
2023-02-15 12:04:03 +00:00
|
|
|
const QString zero = zeroBoolOrInt(to);
|
2022-02-16 12:33:47 +00:00
|
|
|
if (!zero.isEmpty())
|
|
|
|
return zero;
|
2023-02-15 12:04:03 +00:00
|
|
|
if (m_typeResolver->equals(to, m_typeResolver->floatType()))
|
|
|
|
return u"std::numeric_limits<float>::quiet_NaN()"_s;
|
|
|
|
if (m_typeResolver->equals(to, m_typeResolver->realType()))
|
|
|
|
return u"std::numeric_limits<double>::quiet_NaN()"_s;
|
2022-02-16 12:33:47 +00:00
|
|
|
if (m_typeResolver->equals(to, m_typeResolver->stringType()))
|
2022-03-21 09:21:18 +00:00
|
|
|
return QQmlJSUtils::toLiteral(u"undefined"_s);
|
2023-08-21 13:52:39 +00:00
|
|
|
if (m_typeResolver->equals(to, m_typeResolver->varType()))
|
|
|
|
return u"QVariant()"_s;
|
|
|
|
if (m_typeResolver->equals(to, m_typeResolver->jsValueType()))
|
|
|
|
return u"QJSValue();"_s;
|
|
|
|
if (m_typeResolver->equals(to, m_typeResolver->jsPrimitiveType()))
|
|
|
|
return u"QJSPrimitiveValue()"_s;
|
2022-02-16 12:33:47 +00:00
|
|
|
if (m_typeResolver->equals(from, to))
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(from, m_typeResolver->nullType())) {
|
2022-02-03 16:31:33 +00:00
|
|
|
if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(to, jsValueType))
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"QJSValue(QJSValue::NullValue)"_s;
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(to, jsPrimitiveType))
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"QJSPrimitiveValue(QJSPrimitiveNull())"_s;
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(to, varType))
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"QVariant::fromValue<std::nullptr_t>(nullptr)"_s;
|
2023-02-15 12:04:03 +00:00
|
|
|
const QString zero = zeroBoolOrInt(to);
|
2022-02-16 12:33:47 +00:00
|
|
|
if (!zero.isEmpty())
|
|
|
|
return zero;
|
2023-02-15 12:04:03 +00:00
|
|
|
if (m_typeResolver->equals(to, m_typeResolver->floatType()))
|
|
|
|
return u"0.0f"_s;
|
|
|
|
if (m_typeResolver->equals(to, m_typeResolver->realType()))
|
|
|
|
return u"0.0"_s;
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(to, m_typeResolver->stringType()))
|
2022-03-21 09:21:18 +00:00
|
|
|
return QQmlJSUtils::toLiteral(u"null"_s);
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(from, to))
|
2022-02-03 16:31:33 +00:00
|
|
|
return QString();
|
2022-03-21 09:21:18 +00:00
|
|
|
reject(u"Conversion from null to %1"_s.arg(to->internalName()));
|
2022-02-03 16:31:33 +00:00
|
|
|
}
|
2022-02-03 13:45:49 +00:00
|
|
|
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(from, to))
|
2021-11-16 15:49:49 +00:00
|
|
|
return variable;
|
|
|
|
|
2022-01-04 17:59:14 +00:00
|
|
|
if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
|
|
|
|
if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
|
2022-03-23 09:04:22 +00:00
|
|
|
// Compare internalName here. The same C++ type can be exposed muliple times in
|
|
|
|
// different QML types. However, the C++ names have to be unique. We can always
|
|
|
|
// static_cast to those.
|
|
|
|
|
2022-01-04 17:59:14 +00:00
|
|
|
for (QQmlJSScope::ConstPtr base = from; base; base = base->baseType()) {
|
|
|
|
// We still have to cast as other execution paths may result in different types.
|
2022-03-23 09:04:22 +00:00
|
|
|
if (base->internalName() == to->internalName())
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"static_cast<"_s + to->internalName() + u" *>("_s + variable + u')';
|
2022-01-04 17:59:14 +00:00
|
|
|
}
|
|
|
|
for (QQmlJSScope::ConstPtr base = to; base; base = base->baseType()) {
|
2022-03-23 09:04:22 +00:00
|
|
|
if (base->internalName() == from->internalName())
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"static_cast<"_s + to->internalName() + u" *>("_s + variable + u')';
|
2022-01-04 17:59:14 +00:00
|
|
|
}
|
2022-02-04 21:35:23 +00:00
|
|
|
} else if (m_typeResolver->equals(to, m_typeResolver->boolType())) {
|
2022-03-21 09:21:18 +00:00
|
|
|
return u'(' + variable + u" != nullptr)"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auto isJsValue = [&](const QQmlJSScope::ConstPtr &candidate) {
|
2022-02-04 21:35:23 +00:00
|
|
|
return m_typeResolver->equals(candidate, jsValueType) || candidate->isScript();
|
2021-11-16 15:49:49 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if (isJsValue(from) && isJsValue(to))
|
|
|
|
return variable;
|
|
|
|
|
|
|
|
const auto isBoolOrNumber = [&](const QQmlJSScope::ConstPtr &type) {
|
|
|
|
return m_typeResolver->isNumeric(m_typeResolver->globalType(type))
|
2022-02-04 21:35:23 +00:00
|
|
|
|| m_typeResolver->equals(type, m_typeResolver->boolType())
|
2023-05-05 07:30:27 +00:00
|
|
|
|| type->scopeType() == QQmlSA::ScopeType::EnumScope;
|
2021-11-16 15:49:49 +00:00
|
|
|
};
|
|
|
|
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(from, m_typeResolver->realType())
|
2022-07-29 14:25:22 +00:00
|
|
|
|| m_typeResolver->equals(from, m_typeResolver->floatType())) {
|
2023-03-27 16:00:46 +00:00
|
|
|
if (m_typeResolver->isSignedInteger(to))
|
2022-07-29 14:25:22 +00:00
|
|
|
return u"QJSNumberCoercion::toInteger("_s + variable + u')';
|
2023-03-27 16:00:46 +00:00
|
|
|
if (m_typeResolver->isUnsignedInteger(to))
|
2022-07-29 14:25:22 +00:00
|
|
|
return u"uint(QJSNumberCoercion::toInteger("_s + variable + u"))"_s;
|
|
|
|
if (m_typeResolver->equals(to, m_typeResolver->boolType()))
|
|
|
|
return u'(' + variable + u" && !std::isnan("_s + variable + u"))"_s;
|
2022-02-04 21:35:23 +00:00
|
|
|
}
|
2021-11-16 15:49:49 +00:00
|
|
|
|
|
|
|
if (isBoolOrNumber(from) && isBoolOrNumber(to))
|
|
|
|
return to->internalName() + u'(' + variable + u')';
|
|
|
|
|
2022-02-04 21:35:23 +00:00
|
|
|
|
|
|
|
if (m_typeResolver->equals(from, jsPrimitiveType)) {
|
|
|
|
if (m_typeResolver->equals(to, m_typeResolver->realType()))
|
2022-03-21 09:21:18 +00:00
|
|
|
return variable + u".toDouble()"_s;
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(to, boolType))
|
2022-03-21 09:21:18 +00:00
|
|
|
return variable + u".toBoolean()"_s;
|
2024-01-03 09:48:26 +00:00
|
|
|
if (m_typeResolver->isIntegral(to))
|
|
|
|
return u"%1(%2.toInteger())"_s.arg(to->internalName(), variable);
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(to, m_typeResolver->stringType()))
|
2022-03-21 09:21:18 +00:00
|
|
|
return variable + u".toString()"_s;
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(to, jsValueType))
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"QJSValue(QJSPrimitiveValue("_s + variable + u"))"_s;
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(to, varType))
|
2022-03-21 09:21:18 +00:00
|
|
|
return variable + u".toVariant()"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (isJsValue(from)) {
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(to, jsPrimitiveType))
|
2022-03-21 09:21:18 +00:00
|
|
|
return variable + u".toPrimitive()"_s;
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(to, varType))
|
2022-03-21 09:21:18 +00:00
|
|
|
return variable + u".toVariant(QJSValue::RetainJSObjects)"_s;
|
|
|
|
return u"qjsvalue_cast<"_s + castTargetName(to) + u">("_s + variable + u')';
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2022-12-01 16:09:23 +00:00
|
|
|
if (m_typeResolver->equals(to, jsPrimitiveType)) {
|
|
|
|
// null and undefined have been handled above already
|
|
|
|
Q_ASSERT(!m_typeResolver->equals(from, m_typeResolver->nullType()));
|
|
|
|
Q_ASSERT(!m_typeResolver->equals(from, m_typeResolver->voidType()));
|
|
|
|
|
|
|
|
if (m_typeResolver->equals(from, m_typeResolver->boolType())
|
2023-03-27 16:00:46 +00:00
|
|
|
|| m_typeResolver->equals(from, m_typeResolver->int32Type())
|
2022-12-01 16:09:23 +00:00
|
|
|
|| m_typeResolver->equals(from, m_typeResolver->realType())
|
|
|
|
|| m_typeResolver->equals(from, m_typeResolver->stringType())) {
|
|
|
|
return u"QJSPrimitiveValue("_s + variable + u')';
|
2023-03-27 16:00:46 +00:00
|
|
|
} else if (m_typeResolver->isSignedInteger(from)
|
|
|
|
|| m_typeResolver->equals(from, m_typeResolver->uint16Type())
|
|
|
|
|| m_typeResolver->equals(from, m_typeResolver->uint8Type())) {
|
|
|
|
return u"QJSPrimitiveValue(int("_s + variable + u"))"_s;
|
2022-12-01 16:09:23 +00:00
|
|
|
} else if (m_typeResolver->isNumeric(from)) {
|
|
|
|
return u"QJSPrimitiveValue(double("_s + variable + u"))"_s;
|
|
|
|
}
|
|
|
|
}
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(to, jsValueType))
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"aotContext->engine->toScriptValue("_s + variable + u')';
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(from, varType)) {
|
|
|
|
if (m_typeResolver->equals(to, m_typeResolver->listPropertyType()))
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"QQmlListReference("_s + variable + u", aotContext->qmlEngine())"_s;
|
|
|
|
return u"aotContext->engine->fromVariant<"_s + castTargetName(to) + u">("_s
|
2022-02-16 09:47:04 +00:00
|
|
|
+ variable + u')';
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2022-02-04 21:35:23 +00:00
|
|
|
if (m_typeResolver->equals(to, varType))
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"QVariant::fromValue("_s + variable + u')';
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-02-24 15:36:14 +00:00
|
|
|
if (m_typeResolver->equals(from, m_typeResolver->urlType())
|
|
|
|
&& m_typeResolver->equals(to, m_typeResolver->stringType())) {
|
2022-03-21 09:21:18 +00:00
|
|
|
return variable + u".toString()"_s;
|
2022-02-24 15:36:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m_typeResolver->equals(from, m_typeResolver->stringType())
|
|
|
|
&& m_typeResolver->equals(to, m_typeResolver->urlType())) {
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"QUrl("_s + variable + u')';
|
2022-02-24 15:36:14 +00:00
|
|
|
}
|
|
|
|
|
2022-07-18 12:16:11 +00:00
|
|
|
if (m_typeResolver->equals(from, m_typeResolver->byteArrayType())
|
|
|
|
&& m_typeResolver->equals(to, m_typeResolver->stringType())) {
|
|
|
|
return u"QString::fromUtf8("_s + variable + u')';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_typeResolver->equals(from, m_typeResolver->stringType())
|
|
|
|
&& m_typeResolver->equals(to, m_typeResolver->byteArrayType())) {
|
|
|
|
return variable + u".toUtf8()"_s;
|
|
|
|
}
|
|
|
|
|
2023-01-12 09:06:35 +00:00
|
|
|
for (const auto &originType : {
|
|
|
|
m_typeResolver->dateTimeType(),
|
|
|
|
m_typeResolver->dateType(),
|
|
|
|
m_typeResolver->timeType()}) {
|
|
|
|
if (m_typeResolver->equals(from, originType)) {
|
|
|
|
for (const auto &targetType : {
|
|
|
|
m_typeResolver->dateTimeType(),
|
|
|
|
m_typeResolver->dateType(),
|
|
|
|
m_typeResolver->timeType(),
|
2023-08-24 11:48:18 +00:00
|
|
|
m_typeResolver->stringType(),
|
|
|
|
m_typeResolver->realType()}) {
|
2023-01-12 09:06:35 +00:00
|
|
|
if (m_typeResolver->equals(to, targetType)) {
|
|
|
|
return u"aotContext->engine->coerceValue<%1, %2>(%3)"_s.arg(
|
|
|
|
originType->internalName(), targetType->internalName(), variable);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-29 14:25:22 +00:00
|
|
|
const auto retrieveFromPrimitive = [&](
|
|
|
|
const QQmlJSScope::ConstPtr &type, const QString &expression) -> QString
|
2022-02-24 15:36:14 +00:00
|
|
|
{
|
|
|
|
if (m_typeResolver->equals(type, m_typeResolver->boolType()))
|
2022-07-29 14:25:22 +00:00
|
|
|
return expression + u".toBoolean()"_s;
|
2023-03-27 16:00:46 +00:00
|
|
|
if (m_typeResolver->isSignedInteger(type))
|
2022-07-29 14:25:22 +00:00
|
|
|
return expression + u".toInteger()"_s;
|
2023-03-27 16:00:46 +00:00
|
|
|
if (m_typeResolver->isUnsignedInteger(type))
|
2022-07-29 14:25:22 +00:00
|
|
|
return u"uint("_s + expression + u".toInteger())"_s;
|
2022-02-24 15:36:14 +00:00
|
|
|
if (m_typeResolver->equals(type, m_typeResolver->realType()))
|
2022-07-29 14:25:22 +00:00
|
|
|
return expression + u".toDouble()"_s;
|
|
|
|
if (m_typeResolver->equals(type, m_typeResolver->floatType()))
|
|
|
|
return u"float("_s + expression + u".toDouble())"_s;
|
2022-02-24 15:36:14 +00:00
|
|
|
if (m_typeResolver->equals(type, m_typeResolver->stringType()))
|
2022-07-29 14:25:22 +00:00
|
|
|
return expression + u".toString()"_s;
|
2022-02-24 15:36:14 +00:00
|
|
|
return QString();
|
|
|
|
};
|
|
|
|
|
2022-07-29 14:25:22 +00:00
|
|
|
if (!retrieveFromPrimitive(from, u"x"_s).isEmpty()) {
|
|
|
|
const QString retrieve = retrieveFromPrimitive(
|
2023-02-21 10:47:08 +00:00
|
|
|
to, convertStored(from, m_typeResolver->jsPrimitiveType(), variable));
|
2022-02-24 15:36:14 +00:00
|
|
|
if (!retrieve.isEmpty())
|
2022-07-29 14:25:22 +00:00
|
|
|
return retrieve;
|
2022-02-24 15:36:14 +00:00
|
|
|
}
|
|
|
|
|
2022-11-15 10:57:47 +00:00
|
|
|
if (from->isReferenceType() && m_typeResolver->equals(to, m_typeResolver->stringType())) {
|
|
|
|
return u"aotContext->engine->coerceValue<"_s + castTargetName(from) + u", "
|
|
|
|
+ castTargetName(to) + u">("_s + variable + u')';
|
|
|
|
}
|
|
|
|
|
2023-02-23 14:55:52 +00:00
|
|
|
// Any value type is a non-null JS 'object' and therefore coerces to true.
|
|
|
|
if (m_typeResolver->equals(to, m_typeResolver->boolType())) {
|
|
|
|
// All the interesting cases are already handled above:
|
|
|
|
Q_ASSERT(!m_typeResolver->equals(from, m_typeResolver->nullType()));
|
|
|
|
Q_ASSERT(!m_typeResolver->equals(from, m_typeResolver->voidType()));
|
|
|
|
Q_ASSERT(retrieveFromPrimitive(from, u"x"_s).isEmpty());
|
|
|
|
Q_ASSERT(!isBoolOrNumber(from));
|
|
|
|
|
|
|
|
return u"true"_s;
|
|
|
|
}
|
|
|
|
|
2023-04-17 11:17:21 +00:00
|
|
|
if (m_typeResolver->areEquivalentLists(from, to))
|
|
|
|
return variable;
|
|
|
|
|
2023-04-17 10:44:09 +00:00
|
|
|
if (from->isListProperty()
|
|
|
|
&& to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
|
|
|
|
&& to->valueType()->isReferenceType()
|
|
|
|
&& !to->isListProperty()) {
|
|
|
|
return variable + u".toList<"_s + to->internalName() + u">()"_s;
|
|
|
|
}
|
|
|
|
|
2023-02-21 10:47:08 +00:00
|
|
|
bool isExtension = false;
|
2023-06-30 08:14:17 +00:00
|
|
|
if (m_typeResolver->canPopulate(to, from, &isExtension)) {
|
|
|
|
reject(u"populating "_s + to->internalName() + u" from "_s + from->internalName());
|
|
|
|
} else if (const auto ctor = m_typeResolver->selectConstructor(to, from, &isExtension);
|
|
|
|
ctor.isValid()) {
|
2023-02-21 10:47:08 +00:00
|
|
|
const auto argumentTypes = ctor.parameters();
|
|
|
|
return (isExtension ? to->extensionType().scope->internalName() : to->internalName())
|
|
|
|
+ u"("_s + convertStored(from, argumentTypes[0].type(), variable) + u")"_s;
|
|
|
|
}
|
|
|
|
|
2022-06-03 13:12:10 +00:00
|
|
|
// TODO: add more conversions
|
2021-11-16 15:49:49 +00:00
|
|
|
|
2022-06-03 13:12:10 +00:00
|
|
|
reject(u"conversion from "_s + from->internalName() + u" to "_s + to->internalName());
|
|
|
|
return QString();
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2023-02-21 10:47:08 +00:00
|
|
|
QString QQmlJSCodeGenerator::convertContained(const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to, const QString &variable)
|
|
|
|
{
|
|
|
|
const QQmlJSScope::ConstPtr containedFrom = m_typeResolver->containedType(from);
|
|
|
|
const QQmlJSScope::ConstPtr containedTo = m_typeResolver->containedType(to);
|
|
|
|
|
|
|
|
// Those should be handled before, by convertStored().
|
|
|
|
Q_ASSERT(!to.storedType()->isReferenceType());
|
|
|
|
Q_ASSERT(!m_typeResolver->registerIsStoredIn(to, containedTo));
|
2023-03-27 16:00:46 +00:00
|
|
|
Q_ASSERT(!m_typeResolver->isIntegral(from.storedType()));
|
2023-02-21 10:47:08 +00:00
|
|
|
Q_ASSERT(!m_typeResolver->equals(containedFrom, containedTo));
|
|
|
|
|
2023-03-14 13:51:36 +00:00
|
|
|
if (!m_typeResolver->registerIsStoredIn(to, m_typeResolver->varType()) &&
|
|
|
|
!m_typeResolver->registerIsStoredIn(to, m_typeResolver->jsPrimitiveType())) {
|
|
|
|
reject(u"internal conversion into unsupported wrapper type."_s);
|
2023-12-21 10:07:10 +00:00
|
|
|
return QString();
|
2023-03-14 13:51:36 +00:00
|
|
|
}
|
2023-02-21 10:47:08 +00:00
|
|
|
|
|
|
|
bool isExtension = false;
|
2023-06-30 08:14:17 +00:00
|
|
|
if (m_typeResolver->canPopulate(containedTo, containedFrom, &isExtension)) {
|
|
|
|
reject(u"populating "_s + containedTo->internalName()
|
|
|
|
+ u" from "_s + containedFrom->internalName());
|
2023-12-21 10:07:10 +00:00
|
|
|
return QString();
|
2023-06-30 08:14:17 +00:00
|
|
|
} else if (const auto ctor = m_typeResolver->selectConstructor(
|
2023-02-21 10:47:08 +00:00
|
|
|
containedTo, containedFrom, &isExtension); ctor.isValid()) {
|
|
|
|
const auto argumentTypes = ctor.parameters();
|
|
|
|
const QQmlJSScope::ConstPtr argumentType = argumentTypes[0].type();
|
|
|
|
|
|
|
|
// We need to store the converted argument in a temporary
|
|
|
|
// because it might not be an lvalue.
|
|
|
|
|
|
|
|
QString input;
|
|
|
|
QString argPointer;
|
|
|
|
|
|
|
|
if (m_typeResolver->equals(argumentType, containedFrom)) {
|
|
|
|
input = variable;
|
|
|
|
argPointer = contentPointer(from, u"arg"_s);
|
|
|
|
} else {
|
|
|
|
const QQmlJSRegisterContent argument
|
|
|
|
= m_typeResolver->globalType(argumentType)
|
|
|
|
.storedIn(m_typeResolver->genericType(argumentType));
|
|
|
|
input = conversion(from, argument, variable);
|
|
|
|
argPointer = contentPointer(argument, u"arg"_s);
|
|
|
|
}
|
|
|
|
|
|
|
|
return u"[&](){ auto arg = " + input
|
|
|
|
+ u"; return aotContext->constructValueType("_s + metaType(containedTo)
|
|
|
|
+ u", "_s + metaObject(
|
|
|
|
isExtension ? containedTo->extensionType().scope : containedTo)
|
|
|
|
+ u", "_s + QString::number(int(ctor.constructorIndex()))
|
|
|
|
+ u", "_s + argPointer + u"); }()"_s;
|
|
|
|
}
|
|
|
|
|
2023-11-01 17:56:32 +00:00
|
|
|
const auto originalFrom = m_typeResolver->original(from);
|
|
|
|
const auto containedOriginalFrom = m_typeResolver->containedType(originalFrom);
|
|
|
|
if (!m_typeResolver->equals(containedFrom, containedOriginalFrom)
|
|
|
|
&& m_typeResolver->canHold(containedFrom, containedOriginalFrom)) {
|
|
|
|
// If from is simply a wrapping of a specific type into a more general one, we can convert
|
|
|
|
// the original type instead. You can't nest wrappings after all.
|
|
|
|
return conversion(originalFrom.storedIn(from.storedType()), to, variable);
|
|
|
|
}
|
|
|
|
|
2023-12-08 12:55:24 +00:00
|
|
|
if (m_typeResolver->isPrimitive(containedFrom) && m_typeResolver->isPrimitive(containedTo)) {
|
|
|
|
const QQmlJSRegisterContent intermediate = from.storedIn(m_typeResolver->jsPrimitiveType());
|
|
|
|
return conversion(intermediate, to, conversion(from, intermediate, variable));
|
|
|
|
}
|
|
|
|
|
2023-11-01 13:12:53 +00:00
|
|
|
reject(u"internal conversion with incompatible or ambiguous types: %1 -> %2"_s
|
|
|
|
.arg(from.descriptiveName(), to.descriptiveName()));
|
2023-02-21 10:47:08 +00:00
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
void QQmlJSCodeGenerator::reject(const QString &thing)
|
|
|
|
{
|
2022-03-21 09:21:18 +00:00
|
|
|
setError(u"Cannot generate efficient code for %1"_s.arg(thing));
|
2021-11-16 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2022-02-16 13:01:52 +00:00
|
|
|
QQmlJSCodeGenerator::AccumulatorConverter::AccumulatorConverter(QQmlJSCodeGenerator *generator)
|
|
|
|
: accumulatorOut(generator->m_state.accumulatorOut())
|
|
|
|
, accumulatorVariableIn(generator->m_state.accumulatorVariableIn)
|
|
|
|
, accumulatorVariableOut(generator->m_state.accumulatorVariableOut)
|
|
|
|
, generator(generator)
|
|
|
|
{
|
2022-02-16 15:34:56 +00:00
|
|
|
if (accumulatorVariableOut.isEmpty())
|
|
|
|
return;
|
|
|
|
|
2022-02-16 13:01:52 +00:00
|
|
|
const QQmlJSTypeResolver *resolver = generator->m_typeResolver;
|
|
|
|
const QQmlJSScope::ConstPtr origContained = resolver->originalContainedType(accumulatorOut);
|
2022-05-19 13:32:22 +00:00
|
|
|
const QQmlJSScope::ConstPtr stored = accumulatorOut.storedType();
|
|
|
|
const QQmlJSScope::ConstPtr origStored = resolver->originalType(stored);
|
|
|
|
|
|
|
|
// If the stored type differs or if we store in QVariant and the contained type differs,
|
2022-06-27 10:54:47 +00:00
|
|
|
// then we have to use a temporary ...
|
2022-05-19 13:32:22 +00:00
|
|
|
if (!resolver->equals(origStored, stored)
|
|
|
|
|| (!resolver->equals(origContained, resolver->containedType(accumulatorOut))
|
|
|
|
&& resolver->equals(stored, resolver->varType()))) {
|
2022-06-27 10:54:47 +00:00
|
|
|
|
|
|
|
const bool storable = isTypeStorable(resolver, origStored);
|
|
|
|
generator->m_state.accumulatorVariableOut = storable ? u"retrieved"_s : QString();
|
2022-02-16 13:01:52 +00:00
|
|
|
generator->m_state.setRegister(Accumulator, resolver->original(accumulatorOut));
|
2022-03-21 09:21:18 +00:00
|
|
|
generator->m_body += u"{\n"_s;
|
2022-06-27 10:54:47 +00:00
|
|
|
if (storable) {
|
|
|
|
generator->m_body += origStored->augmentedInternalName() + u' '
|
|
|
|
+ generator->m_state.accumulatorVariableOut + u";\n";
|
|
|
|
}
|
2022-02-16 13:01:52 +00:00
|
|
|
} else if (generator->m_state.accumulatorVariableIn == generator->m_state.accumulatorVariableOut
|
|
|
|
&& generator->m_state.readsRegister(Accumulator)
|
|
|
|
&& resolver->registerIsStoredIn(
|
|
|
|
generator->m_state.accumulatorOut(), resolver->varType())) {
|
|
|
|
// If both m_state.accumulatorIn and m_state.accumulatorOut are QVariant, we will need to
|
|
|
|
// prepare the output QVariant, and afterwards use the input variant. Therefore we need to
|
|
|
|
// move the input out of the way first.
|
2022-02-17 12:04:35 +00:00
|
|
|
generator->m_state.accumulatorVariableIn
|
2022-03-21 09:21:18 +00:00
|
|
|
= generator->m_state.accumulatorVariableIn + u"_moved"_s;
|
|
|
|
generator->m_body += u"{\n"_s;
|
|
|
|
generator->m_body += u"QVariant "_s + generator->m_state.accumulatorVariableIn
|
|
|
|
+ u" = std::move("_s + generator->m_state.accumulatorVariableOut + u");\n"_s;
|
2022-02-16 13:01:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QQmlJSCodeGenerator::AccumulatorConverter::~AccumulatorConverter()
|
|
|
|
{
|
|
|
|
if (accumulatorVariableOut != generator->m_state.accumulatorVariableOut) {
|
2022-03-21 09:21:18 +00:00
|
|
|
generator->m_body += accumulatorVariableOut + u" = "_s + generator->conversion(
|
2022-02-16 13:01:52 +00:00
|
|
|
generator->m_state.accumulatorOut(), accumulatorOut,
|
2023-11-22 09:33:59 +00:00
|
|
|
u"std::move("_s + generator->m_state.accumulatorVariableOut + u')') + u";\n"_s;
|
2022-03-21 09:21:18 +00:00
|
|
|
generator->m_body += u"}\n"_s;
|
2022-02-16 13:01:52 +00:00
|
|
|
generator->m_state.setRegister(Accumulator, accumulatorOut);
|
|
|
|
generator->m_state.accumulatorVariableOut = accumulatorVariableOut;
|
|
|
|
} else if (accumulatorVariableIn != generator->m_state.accumulatorVariableIn) {
|
2022-03-21 09:21:18 +00:00
|
|
|
generator->m_body += u"}\n"_s;
|
2022-02-16 13:01:52 +00:00
|
|
|
generator->m_state.accumulatorVariableIn = accumulatorVariableIn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-16 15:49:49 +00:00
|
|
|
QT_END_NAMESPACE
|