Files
RedBear-OS/local/recipes/qt/qtdeclarative/source/tools/qmltc/qmltccompilerpieces.cpp
T
vasilito f31522130f fix: comprehensive boot warnings and exceptions — fixable silenced, unfixable diagnosed
Build system (5 gaps hardened):
- COOKBOOK_OFFLINE defaults to true (fork-mode)
- normalize_patch handles diff -ruN format
- New 'repo validate-patches' command (25/25 relibc patches)
- 14 patched Qt/Wayland/display recipes added to protected list
- relibc archive regenerated with current patch chain

Boot fixes (fixable):
- Full ISO EFI partition: 16 MiB → 1 MiB (matches mini, BIOS hardcoded 2 MiB offset)
- D-Bus system bus: absolute /usr/bin/dbus-daemon path (was skipped)
- redbear-sessiond: absolute /usr/bin/redbear-sessiond path (was skipped)
- daemon framework: silenced spurious INIT_NOTIFY warnings for oneshot_async services (P0-daemon-silence-init-notify.patch)
- udev-shim: demoted INIT_NOTIFY warning to INFO (expected for oneshot_async)
- relibc: comprehensive named semaphores (sem_open/close/unlink) replacing upstream todo!() stubs
- greeterd: Wayland socket timeout 15s → 30s (compositor DRM wait)
- greeter-ui: built and linked (header guard unification, sem_compat stubs removed)
- mc: un-ignored in both configs, fixed glib/libiconv/pcre2 transitive deps
- greeter config: removed stale keymapd dependency from display/greeter services
- prefix toolchain: relibc headers synced, _RELIBC_STDLIB_H guard unified

Unfixable (diagnosed, upstream):
- i2c-hidd: abort on no-I2C-hardware (QEMU) — process::exit → relibc abort
- kded6/greeter-ui: page fault 0x8 — Qt library null deref
- Thread panics fd != -1 — Rust std library on Redox
- DHCP timeout / eth0 MAC — QEMU user-mode networking
- hwrngd/thermald — no hardware RNG/thermal in VM
- live preload allocation — BIOS memory fragmentation, continues on demand
2026-05-05 20:20:37 +01:00

394 lines
17 KiB
C++

// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmltccompilerpieces.h"
#include <private/qqmljsutils_p.h>
#include <tuple>
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
static QString scopeName(const QQmlJSScope::ConstPtr &scope)
{
Q_ASSERT(scope->isFullyResolved());
const auto scopeType = scope->scopeType();
if (scopeType == QQmlSA::ScopeType::GroupedPropertyScope
|| scopeType == QQmlSA::ScopeType::AttachedPropertyScope) {
return scope->baseType()->internalName();
}
return scope->internalName();
}
QmltcCodeGenerator::PreparedValue
QmltcCodeGenerator::wrap_extensionType(const QQmlJSScope::ConstPtr &type,
const QQmlJSMetaProperty &p, const QString &accessor)
{
Q_ASSERT(type->isFullyResolved());
QStringList prologue;
QString value = accessor;
QStringList epilogue;
auto [owner, ownerKind] = QQmlJSScope::ownerOfProperty(type, p.propertyName());
Q_ASSERT(owner);
Q_ASSERT(owner->isFullyResolved());
// properties are only visible when we use QML_{NAMESPACE_}EXTENDED
if (ownerKind == QQmlJSScope::ExtensionType) {
// extensions is a C++-only feature:
Q_ASSERT(!owner->isComposite());
// have to wrap the property into an extension, but we need to figure
// out whether the type is QObject-based or not
prologue << u"{"_s;
const QString extensionObjectName = u"extObject"_s;
if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
// we have a Q_OBJECT. in this case, we call qmlExtendedObject()
// function that should return us the extension object. for that we
// have to figure out which specific extension we want here
int extensionIndex = 0;
auto cppBase = QQmlJSScope::nonCompositeBaseType(type);
for (auto t = cppBase; t; t = t->baseType()) {
if (auto [ext, kind] = t->extensionType(); kind != QQmlJSScope::NotExtension) {
if (ext->isSameType(owner))
break;
++extensionIndex;
}
}
prologue << u"static_assert(std::is_base_of<%1, %2>::value);"_s.arg(u"QObject"_s,
scopeName(type));
prologue << u"auto %1 = qobject_cast<%2 *>(QQmlPrivate::qmlExtendedObject(%3, %4));"_s
.arg(extensionObjectName, owner->internalName(), accessor,
QString::number(extensionIndex));
} else {
// we have a Q_GADGET. the assumption for extension types is that we
// can reinterpret_cast a Q_GADGET object into an extension type
// object and then interact with the extension object right away
prologue << u"static_assert(sizeof(%1) == sizeof(%2));"_s.arg(scopeName(type),
owner->internalName());
prologue << u"static_assert(alignof(%1) == alignof(%2));"_s.arg(scopeName(type),
owner->internalName());
prologue << u"auto %1 = reinterpret_cast<%2 *>(%3);"_s.arg(
extensionObjectName, owner->internalName(), accessor);
}
prologue << u"Q_ASSERT(%1);"_s.arg(extensionObjectName);
value = extensionObjectName;
epilogue << u"}"_s;
}
return { prologue, value, epilogue };
}
void QmltcCodeGenerator::generate_assignToListProperty(
QStringList *block, const QQmlJSScope::ConstPtr &type, const QQmlJSMetaProperty &p,
const QStringList &values, const QString &accessor, QString &qmlListVarName)
{
Q_UNUSED(type); // might be needed
const bool populateLocalListProperty = qmlListVarName.isEmpty();
if (populateLocalListProperty) {
auto [extensionPrologue, extensionAccessor, extensionEpilogue] =
QmltcCodeGenerator::wrap_extensionType(
type, p, QmltcCodeGenerator::wrap_privateClass(accessor, p));
qmlListVarName = u"listprop_%1"_s.arg(p.propertyName());
const QQmlJSScope::ConstPtr elementType = p.type()->elementType();
*block << u"QQmlListProperty<%1> %2;"_s.arg(elementType->internalName(), qmlListVarName);
*block << extensionPrologue;
*block << u"%1 = %2->%3();"_s.arg(qmlListVarName, extensionAccessor, p.read());
*block << extensionEpilogue;
}
for (const QString &value : values) {
auto [prologue, wrappedValue, epilogue] =
QmltcCodeGenerator::wrap_mismatchingTypeConversion(p, value);
*block << prologue;
*block << u"%1.append(std::addressof(%1), %2);"_s.arg(qmlListVarName, wrappedValue);
*block << epilogue;
}
}
void QmltcCodeGenerator::generate_assignToProperty(QStringList *block,
const QQmlJSScope::ConstPtr &type,
const QQmlJSMetaProperty &p,
const QString &value, const QString &accessor,
bool constructFromQObject)
{
Q_ASSERT(block);
Q_ASSERT(p.isValid());
Q_ASSERT(!p.isList()); // NB: this code does not handle list properties
const QString propertyName = p.propertyName();
if (type->hasOwnProperty(p.propertyName()) && !p.isAlias()) {
Q_ASSERT(!p.isPrivate());
// this object is compiled, so just assignment should work fine
auto [prologue, wrappedValue, epilogue] =
QmltcCodeGenerator::wrap_mismatchingTypeConversion(p, value);
*block += prologue;
*block << u"%1->m_%2 = %3;"_s.arg(accessor, propertyName, wrappedValue);
*block += epilogue;
} else if (QString propertySetter = p.write(); !propertySetter.isEmpty()
&& !QQmlJSUtils::bindablePropertyHasDefaultAccessor(
p, QQmlJSUtils::PropertyAccessor_Write)) {
// there's a WRITE function
auto [prologue, wrappedValue, epilogue] =
QmltcCodeGenerator::wrap_mismatchingTypeConversion(p, value);
*block += prologue;
auto [extensionPrologue, extensionAccessor, extensionEpilogue] =
QmltcCodeGenerator::wrap_extensionType(
type, p, QmltcCodeGenerator::wrap_privateClass(accessor, p));
*block += extensionPrologue;
*block << extensionAccessor + u"->" + propertySetter + u"(" + wrappedValue + u");";
*block += extensionEpilogue;
*block += epilogue;
} else {
// this property is weird, fallback to `setProperty`
*block << u"{ // couldn't find property setter, so using QObject::setProperty()"_s;
QString val = value;
if (constructFromQObject) {
const QString variantName = u"var_" + propertyName;
*block << u"QVariant " + variantName + u";";
*block << variantName + u".setValue(" + val + u");";
val = u"std::move(" + variantName + u")";
}
// NB: setProperty() would handle private properties
*block << accessor + u"->setProperty(\"" + propertyName + u"\", " + val + u");";
*block << u"}"_s;
}
}
void QmltcCodeGenerator::generate_setIdValue(QStringList *block, const QString &context,
qsizetype index, const QString &accessor,
const QString &idString)
{
Q_ASSERT(index >= 0);
*block << u"Q_ASSERT(%1 < %2->numIdValues()); // make sure Id is in bounds"_s.arg(index).arg(
context);
*block << u"%1->setIdValue(%2 /* id: %3 */, %4);"_s.arg(context, QString::number(index),
idString, accessor);
}
void QmltcCodeGenerator::generate_callExecuteRuntimeFunction(
QStringList *block, const QString &url, QQmlJSMetaMethod::AbsoluteFunctionIndex index,
const QString &accessor, const QString &returnType, const QList<QmltcVariable> &parameters)
{
*block << u"QQmlEnginePrivate *e = QQmlEnginePrivate::get(qmlEngine(" + accessor + u"));";
const QString returnValueName = u"_ret"_s;
QStringList args;
args.reserve(parameters.size() + 1);
QStringList types;
types.reserve(parameters.size() + 1);
if (returnType == u"void"_s) {
args << u"nullptr"_s;
types << u"QMetaType::fromType<void>()"_s;
} else {
*block << returnType + u" " + returnValueName + u"{};"; // TYPE _ret{};
args << u"const_cast<void *>(reinterpret_cast<const void *>(std::addressof("
+ returnValueName + u")))";
types << u"QMetaType::fromType<std::decay_t<" + returnType + u">>()";
}
for (const QmltcVariable &p : parameters) {
args << u"const_cast<void *>(reinterpret_cast<const void *>(std::addressof(" + p.name
+ u")))";
types << u"QMetaType::fromType<std::decay_t<" + p.cppType + u">>()";
}
*block << u"void *_a[] = { " + args.join(u", "_s) + u" };";
*block << u"QMetaType _t[] = { " + types.join(u", "_s) + u" };";
const qsizetype runtimeIndex = static_cast<qsizetype>(index);
Q_ASSERT(runtimeIndex >= 0);
*block << u"e->executeRuntimeFunction(" + url + u", " + QString::number(runtimeIndex) + u", "
+ accessor + u", " + QString::number(parameters.size()) + u", _a, _t);";
if (returnType != u"void"_s)
*block << u"return " + returnValueName + u";";
}
void QmltcCodeGenerator::generate_createBindingOnProperty(
QStringList *block, const QString &unitVarName, const QString &scope,
qsizetype functionIndex, const QString &target, const QQmlJSScope::ConstPtr &targetType,
int propertyIndex, const QQmlJSMetaProperty &p, int valueTypeIndex,
const QString &subTarget)
{
const QString propName = QQmlJSUtils::toLiteral(p.propertyName());
if (QString bindable = p.bindable(); !bindable.isEmpty()) {
// TODO: test that private properties are bindable
QString createBindingForBindable = u"QT_PREPEND_NAMESPACE(QQmlCppBinding)::"
u"createBindingForBindable("
+ unitVarName + u", " + scope + u", " + QString::number(functionIndex) + u", "
+ target + u", " + QString::number(propertyIndex) + u", "
+ QString::number(valueTypeIndex) + u", " + propName + u")";
const QString accessor = (valueTypeIndex == -1) ? target : subTarget;
QStringList prologue;
QString value = QmltcCodeGenerator::wrap_privateClass(accessor, p);
QStringList epilogue;
if (targetType) {
auto [pro, v, epi] = QmltcCodeGenerator::wrap_extensionType(targetType, p, value);
std::tie(prologue, value, epilogue) = std::make_tuple(pro, v, epi);
}
*block += prologue;
*block << u"if (!initializedCache.contains(QStringLiteral(\"%1\")))"_s.arg(p.propertyName());
*block << u" "_s + value + u"->" + bindable + u"().setBinding(" + createBindingForBindable + u");";
*block += epilogue;
} else {
QString createBindingForNonBindable =
u" "_s
+ u"QT_PREPEND_NAMESPACE(QQmlCppBinding)::createBindingForNonBindable(" + unitVarName
+ u", " + scope + u", " + QString::number(functionIndex) + u", " + target + u", "
+ QString::number(propertyIndex) + u", " + QString::number(valueTypeIndex) + u", "
+ propName + u")";
// Note: in this version, the binding is set implicitly
*block << u"if (!initializedCache.contains(QStringLiteral(\"%1\")))"_s.arg(p.propertyName());
*block << createBindingForNonBindable + u";";
}
}
static QByteArray toLiteral(const QByteArray &utf8)
{
return QQmlJSUtils::toLiteral<QByteArray, char, QByteArrayView>(utf8);
}
static QString serializeTranslation(const QQmlTranslation::QsTrIdData &data)
{
return QStringLiteral(R"(QQmlTranslation(QQmlTranslation::QsTrIdData(
%1,
%4)))")
.arg(toLiteral(data.id()))
.arg(data.number());
}
static QString serializeTranslation(const QQmlTranslation::QsTrData &data)
{
return QStringLiteral(R"(QQmlTranslation(QQmlTranslation::QsTrData(
%1,
%2,
%3,
%4)))")
.arg(toLiteral(data.context()),
toLiteral(data.text()),
toLiteral(data.comment()))
.arg(data.number());
}
static QString serializeTranslation(const QQmlTranslation &translation)
{
return translation.visit(
[](auto &&arg) -> QString {
using T = std::decay_t<decltype(arg)>;
if constexpr (!std::is_same_v<T, std::nullptr_t>)
return serializeTranslation(arg);
else {
Q_ASSERT_X(false, "QQmlTranslation", "Uninitialized Translation");
return {};
}
});
}
void QmltcCodeGenerator::generate_createTranslationBindingOnProperty(
QStringList *block, const TranslationBindingInfo &info)
{
const QString propName = QQmlJSUtils::toLiteral(info.property.propertyName());
const QString qqmlTranslation = serializeTranslation(info.data);
if (QString bindable = info.property.bindable(); !bindable.isEmpty()) {
// TODO: test that private properties are bindable
QString createTranslationCode = uR"(QT_PREPEND_NAMESPACE(QQmlCppBinding)
::createTranslationBindingForBindable(%1, %2, %3, %4, %5))"_s
.arg(info.unitVarName, info.target)
.arg(info.propertyIndex)
.arg(qqmlTranslation, propName);
*block << QmltcCodeGenerator::wrap_privateClass(info.target, info.property) + u"->"
+ bindable + u"().setBinding(" + createTranslationCode + u");";
} else {
QString locationString =
u"QQmlSourceLocation(%1->fileName(), %2, %3)"_s.arg(info.unitVarName)
.arg(info.line)
.arg(info.column);
QString createTranslationCode = uR"(QT_PREPEND_NAMESPACE(QQmlCppBinding)
::createTranslationBindingForNonBindable(
%1, //unit
%2, //location
%3, //translationData
%4, //thisObject
%5, //bindingTarget
%6, //metaPropertyIndex
%7, //propertyName
%8) //valueTypePropertyIndex
)"_s.arg(info.unitVarName, locationString, qqmlTranslation, info.scope, info.target)
.arg(info.propertyIndex)
.arg(propName)
.arg(info.valueTypeIndex);
// Note: in this version, the binding is set implicitly
*block << createTranslationCode + u";";
}
}
QmltcCodeGenerator::PreparedValue
QmltcCodeGenerator::wrap_mismatchingTypeConversion(const QQmlJSMetaProperty &p, QString value)
{
auto isDerivedFromBuiltin = [](const QQmlJSScope::ConstPtr &derived, const QString &builtin) {
for (QQmlJSScope::ConstPtr t = derived; t; t = t->baseType()) {
if (t->internalName() == builtin)
return true;
}
return false;
};
QStringList prologue;
QStringList epilogue;
const QQmlJSScope::ConstPtr propType = p.type();
if (isDerivedFromBuiltin(propType, u"QVariant"_s)) {
const QString variantName = u"var_" + p.propertyName();
prologue << u"{ // accepts QVariant"_s;
prologue << u"QVariant " + variantName + u";";
prologue << variantName + u".setValue(" + value + u");";
epilogue << u"}"_s;
value = u"std::move(" + variantName + u")";
} else if (isDerivedFromBuiltin(propType, u"QJSValue"_s)) {
const QString jsvalueName = u"jsvalue_" + p.propertyName();
prologue << u"{ // accepts QJSValue"_s;
// Note: do not assume we have the engine, acquire it from `this`
prologue << u"auto e = qmlEngine(this);"_s;
prologue << u"QJSValue " + jsvalueName + u" = e->toScriptValue(" + value + u");";
epilogue << u"}"_s;
value = u"std::move(" + jsvalueName + u")";
}
return { prologue, value, epilogue };
}
QString QmltcCodeGenerator::wrap_privateClass(const QString &accessor, const QQmlJSMetaProperty &p)
{
if (!p.isPrivate())
return accessor;
const QString privateType = p.privateClass();
return u"static_cast<" + privateType + u" *>(QObjectPrivate::get(" + accessor + u"))";
}
QString QmltcCodeGenerator::wrap_qOverload(const QList<QmltcVariable> &parameters,
const QString &overloaded)
{
QStringList types;
types.reserve(parameters.size());
for (const QmltcVariable &p : parameters)
types.emplaceBack(p.cppType);
return u"qOverload<" + types.join(u", "_s) + u">(" + overloaded + u")";
}
QString QmltcCodeGenerator::wrap_addressof(const QString &addressed)
{
return u"std::addressof(" + addressed + u")";
}
QT_END_NAMESPACE