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
409 lines
16 KiB
C++
409 lines
16 KiB
C++
// Copyright (C) 2020 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
#include "qqmldomtop_p.h"
|
|
#include "qqmldomelements_p.h"
|
|
#include "qqmldom_utils_p.h"
|
|
|
|
#include <QtCore/QDir>
|
|
#include <QtCore/QFileInfo>
|
|
#include <QtCore/QScopeGuard>
|
|
|
|
#include <memory>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
namespace QQmlJS {
|
|
namespace Dom {
|
|
|
|
static ErrorGroups myVersioningErrors()
|
|
{
|
|
static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("Exports"),
|
|
NewErrorGroup("Version") } };
|
|
return res;
|
|
}
|
|
|
|
static ErrorGroups myExportErrors()
|
|
{
|
|
static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("Exports") } };
|
|
return res;
|
|
}
|
|
|
|
bool ModuleScope::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
|
{
|
|
bool cont = true;
|
|
cont = cont && self.invokeVisitorOnValue(visitor, PathEls::Field(Fields::uri), uri);
|
|
cont = cont && self.invokeVisitorOnField(visitor, Fields::version, version);
|
|
cont = cont && visitor(PathEls::Field(Fields::exports), [this, &self]() {
|
|
int minorVersion = version.minorVersion;
|
|
return self.subMapItem(Map(
|
|
self.pathFromOwner().withField(Fields::exports),
|
|
[minorVersion](const DomItem &mapExp, const QString &name) -> DomItem {
|
|
DomItem mapExpOw = mapExp.owner();
|
|
QList<DomItem> exports =
|
|
mapExp.ownerAs<ModuleIndex>()->exportsWithNameAndMinorVersion(
|
|
mapExpOw, name, minorVersion);
|
|
return mapExp.subListItem(List::fromQList<DomItem>(
|
|
mapExp.pathFromOwner().withKey(name), exports,
|
|
[](const DomItem &, const PathEls::PathComponent &,
|
|
const DomItem &el) { return el; },
|
|
ListOptions::Normal));
|
|
},
|
|
[](const DomItem &mapExp) {
|
|
DomItem mapExpOw = mapExp.owner();
|
|
return mapExp.ownerAs<ModuleIndex>()->exportNames(mapExpOw);
|
|
},
|
|
QLatin1String("List<Exports>")));
|
|
});
|
|
cont = cont && visitor(PathEls::Field(Fields::symbols), [&self]() {
|
|
Path basePath = Path::fromCurrent(PathCurrent::Obj).withField(Fields::exports);
|
|
return self.subMapItem(Map(
|
|
self.pathFromOwner().withField(Fields::symbols),
|
|
[basePath](const DomItem &mapExp, const QString &name) -> DomItem {
|
|
QList<Path> symb({ basePath.withKey(name) });
|
|
return mapExp.subReferencesItem(PathEls::Key(name), symb);
|
|
},
|
|
[](const DomItem &mapExp) {
|
|
DomItem mapExpOw = mapExp.owner();
|
|
return mapExp.ownerAs<ModuleIndex>()->exportNames(mapExpOw);
|
|
},
|
|
QLatin1String("List<References>")));
|
|
});
|
|
cont = cont && visitor(PathEls::Field(Fields::autoExports), [this, &self]() {
|
|
return containingObject(self).field(Fields::autoExports);
|
|
});
|
|
return cont;
|
|
}
|
|
|
|
std::shared_ptr<OwningItem> ModuleIndex::doCopy(const DomItem &) const
|
|
{
|
|
return std::make_shared<ModuleIndex>(*this);
|
|
}
|
|
|
|
ModuleIndex::ModuleIndex(const ModuleIndex &o)
|
|
: OwningItem(o), m_uri(o.uri()), m_majorVersion(o.majorVersion())
|
|
{
|
|
QMap<int, ModuleScope *> scopes;
|
|
{
|
|
QMutexLocker l2(o.mutex());
|
|
m_qmltypesFilesPaths += o.m_qmltypesFilesPaths;
|
|
m_qmldirPaths += o.m_qmldirPaths;
|
|
m_directoryPaths += o.m_directoryPaths;
|
|
scopes = o.m_moduleScope;
|
|
}
|
|
auto it = scopes.begin();
|
|
auto end = scopes.end();
|
|
while (it != end) {
|
|
ensureMinorVersion((*it)->version.minorVersion);
|
|
++it;
|
|
}
|
|
}
|
|
|
|
ModuleIndex::~ModuleIndex()
|
|
{
|
|
QMap<int, ModuleScope *> scopes;
|
|
{
|
|
QMutexLocker l(mutex());
|
|
scopes = m_moduleScope;
|
|
m_moduleScope.clear();
|
|
}
|
|
auto it = scopes.begin();
|
|
auto end = scopes.end();
|
|
while (it != end) {
|
|
delete *it;
|
|
++it;
|
|
}
|
|
}
|
|
|
|
bool ModuleIndex::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
|
{
|
|
bool cont = self.invokeVisitorOnValue(visitor, PathEls::Field(Fields::uri), uri());
|
|
cont = cont && self.invokeVisitorOnValue(visitor, PathEls::Field(Fields::majorVersion),
|
|
majorVersion());
|
|
cont = cont && visitor(PathEls::Field(Fields::moduleScope), [this, &self]() {
|
|
return self.subMapItem(Map(
|
|
pathFromOwner().withField(Fields::moduleScope),
|
|
[](const DomItem &map, const QString &minorVersionStr) {
|
|
bool ok;
|
|
int minorVersion = minorVersionStr.toInt(&ok);
|
|
if (minorVersionStr.isEmpty()
|
|
|| minorVersionStr.compare(u"Latest", Qt::CaseInsensitive) == 0)
|
|
minorVersion = Version::Latest;
|
|
else if (!ok)
|
|
return DomItem();
|
|
return map.copy(
|
|
map.ownerAs<ModuleIndex>()->ensureMinorVersion(minorVersion));
|
|
},
|
|
[this](const DomItem &) {
|
|
QSet<QString> res;
|
|
for (int el : minorVersions())
|
|
if (el >= 0)
|
|
res.insert(QString::number(el));
|
|
if (!minorVersions().isEmpty())
|
|
res.insert(QString());
|
|
return res;
|
|
},
|
|
QLatin1String("Map<List<Exports>>")));
|
|
});
|
|
cont = cont && visitor(PathEls::Field(Fields::sources), [this, &self]() {
|
|
return self.subReferencesItem(PathEls::Field(Fields::sources), sources());
|
|
});
|
|
cont = cont && self.invokeVisitorOnLazyField(visitor, Fields::autoExports, [this, &self]() {
|
|
return autoExports(self);
|
|
});
|
|
return cont;
|
|
}
|
|
|
|
QSet<QString> ModuleIndex::exportNames(const DomItem &self) const
|
|
{
|
|
QSet<QString> res;
|
|
QList<Path> mySources = sources();
|
|
for (int i = 0; i < mySources.size(); ++i) {
|
|
DomItem source = self.path(mySources.at(i));
|
|
res += source.field(Fields::exports).keys();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
QList<DomItem> ModuleIndex::autoExports(const DomItem &self) const
|
|
{
|
|
QList<DomItem> res;
|
|
Path selfPath = canonicalPath(self).withField(Fields::autoExports);
|
|
RefCacheEntry cached = RefCacheEntry::forPath(self, selfPath);
|
|
QList<Path> cachedPaths;
|
|
switch (cached.cached) {
|
|
case RefCacheEntry::Cached::None:
|
|
case RefCacheEntry::Cached::First:
|
|
break;
|
|
case RefCacheEntry::Cached::All:
|
|
cachedPaths += cached.canonicalPaths;
|
|
if (cachedPaths.isEmpty())
|
|
return res;
|
|
}
|
|
DomItem env = self.environment();
|
|
if (!cachedPaths.isEmpty()) {
|
|
bool outdated = false;
|
|
for (const Path &p : cachedPaths) {
|
|
DomItem newEl = env.path(p);
|
|
if (!newEl) {
|
|
outdated = true;
|
|
qWarning() << "referenceCache outdated, reference at " << selfPath
|
|
<< " leads to invalid path " << p;
|
|
break;
|
|
} else {
|
|
res.append(newEl);
|
|
}
|
|
}
|
|
if (outdated) {
|
|
res.clear();
|
|
} else {
|
|
return res;
|
|
}
|
|
}
|
|
QList<Path> mySources = sources();
|
|
QSet<QString> knownAutoImportUris;
|
|
QList<ModuleAutoExport> knownExports;
|
|
for (const Path &p : mySources) {
|
|
DomItem autoExports = self.path(p).field(Fields::autoExports);
|
|
for (const DomItem &i : autoExports.values()) {
|
|
if (const ModuleAutoExport *iPtr = i.as<ModuleAutoExport>()) {
|
|
if (!knownAutoImportUris.contains(iPtr->import.uri.toString())
|
|
|| !knownExports.contains(*iPtr)) {
|
|
knownAutoImportUris.insert(iPtr->import.uri.toString());
|
|
knownExports.append(*iPtr);
|
|
res.append(i);
|
|
cachedPaths.append(i.canonicalPath());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
RefCacheEntry::addForPath(self, selfPath,
|
|
RefCacheEntry { RefCacheEntry::Cached::All, cachedPaths });
|
|
return res;
|
|
}
|
|
|
|
QList<DomItem> ModuleIndex::exportsWithNameAndMinorVersion(const DomItem &self, const QString &name,
|
|
int minorVersion) const
|
|
{
|
|
Path myPath = Paths::moduleScopePath(uri(), Version(majorVersion(), minorVersion))
|
|
.withField(Fields::exports)
|
|
.withKey(name);
|
|
QList<Path> mySources = sources();
|
|
QList<DomItem> res;
|
|
QList<DomItem> undef;
|
|
if (minorVersion < 0)
|
|
minorVersion = std::numeric_limits<int>::max();
|
|
int vNow = Version::Undefined;
|
|
for (int i = 0; i < mySources.size(); ++i) {
|
|
DomItem source = self.path(mySources.at(i));
|
|
DomItem exports = source.field(Fields::exports).key(name);
|
|
int nExports = exports.indexes();
|
|
if (nExports == 0)
|
|
continue;
|
|
for (int j = 0; j < nExports; ++j) {
|
|
DomItem exportItem = exports.index(j);
|
|
if (!exportItem)
|
|
continue;
|
|
Version const *versionPtr = exportItem.field(Fields::version).as<Version>();
|
|
if (versionPtr == nullptr || !versionPtr->isValid()) {
|
|
undef.append(exportItem);
|
|
} else {
|
|
if (majorVersion() < 0)
|
|
self.addError(std::move(myVersioningErrors()
|
|
.error(tr("Module %1 (unversioned) has versioned entries "
|
|
"for '%2' from %3")
|
|
.arg(uri(), name,
|
|
source.canonicalPath().toString()))
|
|
.withPath(myPath)));
|
|
if ((versionPtr->majorVersion == majorVersion()
|
|
|| versionPtr->majorVersion == Version::Undefined)
|
|
&& versionPtr->minorVersion >= vNow
|
|
&& versionPtr->minorVersion <= minorVersion) {
|
|
if (versionPtr->minorVersion > vNow)
|
|
res.clear();
|
|
res.append(exportItem);
|
|
vNow = versionPtr->minorVersion;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!undef.isEmpty()) {
|
|
if (!res.isEmpty()) {
|
|
self.addError(std::move(myVersioningErrors()
|
|
.error(tr("Module %1 (major version %2) has versioned and "
|
|
"unversioned entries for '%3'")
|
|
.arg(uri(), QString::number(majorVersion()), name))
|
|
.withPath(myPath)));
|
|
return res + undef;
|
|
} else {
|
|
return undef;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
QList<Path> ModuleIndex::sources() const
|
|
{
|
|
QList<Path> res;
|
|
QMutexLocker l(mutex());
|
|
res += m_qmltypesFilesPaths;
|
|
if (!m_qmldirPaths.isEmpty())
|
|
res += m_qmldirPaths.first();
|
|
else if (!m_directoryPaths.isEmpty())
|
|
res += m_directoryPaths.first();
|
|
return res;
|
|
}
|
|
|
|
ModuleScope *ModuleIndex::ensureMinorVersion(int minorVersion)
|
|
{
|
|
if (minorVersion < 0)
|
|
minorVersion = Version::Latest;
|
|
{
|
|
QMutexLocker l(mutex());
|
|
auto it = m_moduleScope.constFind(minorVersion);
|
|
if (it != m_moduleScope.cend())
|
|
return *it;
|
|
}
|
|
ModuleScope *res = nullptr;
|
|
ModuleScope *newScope = new ModuleScope(m_uri, Version(majorVersion(), minorVersion));
|
|
auto cleanup = qScopeGuard([&newScope] { delete newScope; });
|
|
{
|
|
QMutexLocker l(mutex());
|
|
auto it = m_moduleScope.constFind(minorVersion);
|
|
if (it != m_moduleScope.cend()) {
|
|
res = *it;
|
|
} else {
|
|
res = newScope;
|
|
newScope = nullptr;
|
|
m_moduleScope.insert(minorVersion, res);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void ModuleIndex::mergeWith(const std::shared_ptr<ModuleIndex> &o)
|
|
{
|
|
if (o) {
|
|
QList<Path> qmltypesPaths;
|
|
QMap<int, ModuleScope *> scopes;
|
|
{
|
|
QMutexLocker l2(o->mutex());
|
|
qmltypesPaths = o->m_qmltypesFilesPaths;
|
|
scopes = o->m_moduleScope;
|
|
}
|
|
{
|
|
QMutexLocker l(mutex());
|
|
for (const Path &qttPath : qmltypesPaths) {
|
|
if (!m_qmltypesFilesPaths.contains((qttPath)))
|
|
m_qmltypesFilesPaths.append(qttPath);
|
|
}
|
|
}
|
|
auto it = scopes.begin();
|
|
auto end = scopes.end();
|
|
while (it != end) {
|
|
ensureMinorVersion((*it)->version.minorVersion);
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
|
|
QList<Path> ModuleIndex::qmldirsToLoad(const DomItem &self)
|
|
{
|
|
// this always checks the filesystem to the qmldir file to load
|
|
DomItem env = self.environment();
|
|
std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>();
|
|
QStringList subPathComponents = uri().split(u'.');
|
|
QString subPath = subPathComponents.join(u'/');
|
|
QString logicalPath;
|
|
QString subPathV = subPath + QChar::fromLatin1('.') + QString::number(majorVersion())
|
|
+ QLatin1String("/qmldir");
|
|
QString dirPath;
|
|
if (majorVersion() >= 0) {
|
|
qCDebug(QQmlJSDomImporting)
|
|
<< "ModuleIndex::qmldirsToLoad: Searching versioned module" << subPath
|
|
<< majorVersion() << "in" << envPtr->loadPaths().join(u", ");
|
|
for (const QString &path : envPtr->loadPaths()) {
|
|
QDir dir(path);
|
|
QFileInfo fInfo(dir.filePath(subPathV));
|
|
if (fInfo.isFile()) {
|
|
qCDebug(QQmlJSDomImporting)
|
|
<< "Found versioned module in " << fInfo.canonicalFilePath();
|
|
logicalPath = subPathV;
|
|
dirPath = fInfo.canonicalFilePath();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (dirPath.isEmpty()) {
|
|
qCDebug(QQmlJSDomImporting) << "ModuleIndex::qmldirsToLoad: Searching unversioned module"
|
|
<< subPath << "in" << envPtr->loadPaths().join(u", ");
|
|
for (const QString &path : envPtr->loadPaths()) {
|
|
QDir dir(path);
|
|
QFileInfo fInfo(dir.filePath(subPath + QLatin1String("/qmldir")));
|
|
if (fInfo.isFile()) {
|
|
qCDebug(QQmlJSDomImporting)
|
|
<< "Found unversioned module in " << fInfo.canonicalFilePath();
|
|
logicalPath = subPath + QLatin1String("/qmldir");
|
|
dirPath = fInfo.canonicalFilePath();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!dirPath.isEmpty()) {
|
|
QMutexLocker l(mutex());
|
|
m_qmldirPaths = QList<Path>({ Paths::qmldirFilePath(dirPath) });
|
|
} else if (uri() != u"QML") {
|
|
const QString loadPaths = envPtr->loadPaths().join(u", "_s);
|
|
qCDebug(QQmlJSDomImporting) << "ModuleIndex::qmldirsToLoad: qmldir at"
|
|
<< (uri() + u"/qmldir"_s) << " was not found in " << loadPaths;
|
|
addErrorLocal(
|
|
myExportErrors()
|
|
.warning(tr("Failed to find main qmldir file for %1 %2 in %3.")
|
|
.arg(uri(), QString::number(majorVersion()), loadPaths))
|
|
.handle());
|
|
}
|
|
return qmldirPaths();
|
|
}
|
|
|
|
} // end namespace Dom
|
|
} // end namespace QQmlJS
|
|
QT_END_NAMESPACE
|