f31522130f
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
394 lines
17 KiB
C++
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> ¶meters)
|
|
{
|
|
*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> ¶meters,
|
|
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
|