Revision control
Copy as Markdown
// Copyright (C) 2019 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
// Qt-Security score:significant
#ifndef QV4EXECUTABLECOMPILATIONUNIT_P_H
#define QV4EXECUTABLECOMPILATIONUNIT_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <private/qintrusivelist_p.h>
#include <private/qqmlmetatype_p.h>
#include <private/qqmlnullablevalue_p.h>
#include <private/qqmlpropertycachevector_p.h>
#include <private/qqmlrefcount_p.h>
#include <private/qqmltype_p.h>
#include <private/qqmltypenamecache_p.h>
#include <private/qv4compileddata_p.h>
#include <private/qv4identifierhash_p.h>
#include <memory>
QT_BEGIN_NAMESPACE
class QQmlScriptData;
class QQmlEnginePrivate;
namespace QV4 {
class CompilationUnitMapper;
struct CompilationUnitRuntimeData
{
Heap::String **runtimeStrings = nullptr; // Array
// pointers either to data->constants() or little-endian memory copy.
// We keep this member twice so that the JIT can access it via standard layout.
const StaticValue *constants = nullptr;
QV4::StaticValue *runtimeRegularExpressions = nullptr;
Heap::InternalClass **runtimeClasses = nullptr;
const StaticValue **imports = nullptr;
QV4::Lookup *runtimeLookups = nullptr;
QList<QV4::Function *> runtimeFunctions;
QList<QV4::Heap::InternalClass *> runtimeBlocks;
mutable QList<QV4::Heap::Object *> templateObjects;
};
static_assert(std::is_standard_layout_v<CompilationUnitRuntimeData>);
static_assert(offsetof(CompilationUnitRuntimeData, runtimeStrings) == 0);
static_assert(offsetof(CompilationUnitRuntimeData, constants) == sizeof(QV4::Heap::String **));
static_assert(offsetof(CompilationUnitRuntimeData, runtimeRegularExpressions) == offsetof(CompilationUnitRuntimeData, constants) + sizeof(const StaticValue *));
static_assert(offsetof(CompilationUnitRuntimeData, runtimeClasses) == offsetof(CompilationUnitRuntimeData, runtimeRegularExpressions) + sizeof(const StaticValue *));
static_assert(offsetof(CompilationUnitRuntimeData, imports) == offsetof(CompilationUnitRuntimeData, runtimeClasses) + sizeof(const StaticValue *));
class Q_QML_EXPORT ExecutableCompilationUnit final
: public CompilationUnitRuntimeData,
public QQmlRefCounted<ExecutableCompilationUnit>
{
Q_DISABLE_COPY_MOVE(ExecutableCompilationUnit)
public:
friend class QQmlRefCounted<ExecutableCompilationUnit>;
friend class QQmlRefPointer<ExecutableCompilationUnit>;
friend struct ExecutionEngine;
ExecutionEngine *engine = nullptr;
QString finalUrlString() const { return m_compilationUnit->finalUrlString(); }
QString fileName() const { return m_compilationUnit->fileName(); }
QUrl url() const { return m_compilationUnit->url(); }
QUrl finalUrl() const { return m_compilationUnit->finalUrl(); }
QQmlRefPointer<QQmlTypeNameCache> typeNameCache() const
{
return m_compilationUnit->typeNameCache;
}
QQmlPropertyCacheVector *propertyCachesPtr()
{
return &m_compilationUnit->propertyCaches;
}
QQmlPropertyCache::ConstPtr rootPropertyCache() const
{
return m_compilationUnit->rootPropertyCache();
}
// mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects
// this is initialized on-demand by QQmlContextData
QHash<int, IdentifierHash> namedObjectsPerComponentCache;
inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex);
ResolvedTypeReference *resolvedType(int id) const
{
return m_compilationUnit->resolvedType(id);
}
QQmlType qmlTypeForComponent(const QString &inlineComponentName = QString()) const
{
return m_compilationUnit->qmlTypeForComponent(inlineComponentName);
}
QMetaType metaType() const { return m_compilationUnit->qmlType.typeId(); }
int inlineComponentId(const QString &inlineComponentName) const
{
return m_compilationUnit->inlineComponentId(inlineComponentName);
}
// --- interface for QQmlPropertyCacheCreator
using CompiledObject = CompiledData::CompilationUnit::CompiledObject;
using CompiledFunction = CompiledData::CompilationUnit::CompiledFunction;
using CompiledBinding = CompiledData::CompilationUnit::CompiledBinding;
using IdToObjectMap = CompiledData::CompilationUnit::IdToObjectMap;
bool nativeMethodsAcceptThisObjects() const
{
return m_compilationUnit->nativeMethodsAcceptThisObjects();
}
bool ignoresFunctionSignature() const { return m_compilationUnit->ignoresFunctionSignature(); }
bool valueTypesAreCopied() const { return m_compilationUnit->valueTypesAreCopied(); }
bool valueTypesAreAddressable() const { return m_compilationUnit->valueTypesAreAddressable(); }
bool valueTypesAreAssertable() const { return m_compilationUnit->valueTypesAreAssertable(); }
bool componentsAreBound() const { return m_compilationUnit->componentsAreBound(); }
bool isESModule() const { return m_compilationUnit->isESModule(); }
int objectCount() const { return m_compilationUnit->objectCount(); }
int resolvedIndex(int index) const { return m_compilationUnit->resolvedIndex(index); }
int implicitComponentForObject(int index) const
{
return m_compilationUnit->implicitComponentForObject(index);
}
const CompiledObject *objectAt(int index) const
{
return m_compilationUnit->objectAt(index);
}
bool objectHasDeferredBindings(int objectIndex) const
{
return m_compilationUnit->objectHasDeferredBindings(objectIndex);
}
bool objectHasCustomParserBindings(int objectIndex) const
{
return m_compilationUnit->objectHasCustomParserBindings(objectIndex);
}
bool objectIsPopulatedByDeferredBinding(int objectIndex) const
{
return m_compilationUnit->objectIsPopulatedByDeferredBinding(objectIndex);
}
bool bindingIsDeferred(int objectIndex, int bindingIndex) const
{
return m_compilationUnit->bindingIsDeferred(objectIndex, bindingIndex);
}
bool bindingIsCustomParser(int objectIndex, int bindingIndex) const
{
return m_compilationUnit->bindingIsCustomParser(objectIndex, bindingIndex);
}
bool bindingIsSignalHandlerExpression(int objectIndex, int bindingIndex) const
{
return m_compilationUnit->bindingIsSignalHandlerExpression(objectIndex, bindingIndex);
}
bool bindingIsSignalHandlerObject(int objectIndex, int bindingIndex) const
{
return m_compilationUnit->bindingIsSignalHandlerObject(objectIndex, bindingIndex);
}
bool bindingIsPropertyObserver(int objectIndex, int bindingIndex) const
{
return m_compilationUnit->bindingIsPropertyObserver(objectIndex, bindingIndex);
}
bool bindingIsSignalHandler(int objectIndex, int bindingIndex) const
{
return m_compilationUnit->bindingIsSignalHandler(objectIndex, bindingIndex);
}
std::optional<int> bindingFoldedEnum(int objectIndex, int bindingIndex) const
{
return m_compilationUnit->bindingFoldedEnum(objectIndex, bindingIndex);
}
const CompiledData::RuntimeBindingIndex *bindingApplicationOrder(int objectIndex) const
{
return m_compilationUnit->bindingApplicationOrder(objectIndex);
}
Heap::Object *templateObjectAt(int index) const;
Heap::Module *instantiate();
const Value *resolveExport(QV4::String *exportName) const
{
QList<ResolveSetEntry> resolveSet;
return resolveExportRecursively(exportName, &resolveSet);
}
QStringList exportedNames() const
{
QStringList names;
QList<const ExecutableCompilationUnit*> exportNameSet;
getExportedNamesRecursively(&names, &exportNameSet);
names.sort();
auto last = std::unique(names.begin(), names.end());
names.erase(last, names.end());
return names;
}
void evaluate();
void evaluateModuleRequests();
void mark(MarkStack *markStack) const { markObjects(markStack); }
void markObjects(MarkStack *markStack) const;
QString bindingValueAsString(const CompiledData::Binding *binding) const;
double bindingValueAsNumber(const CompiledData::Binding *binding) const
{
return m_compilationUnit->bindingValueAsNumber(binding);
}
QString bindingValueAsScriptString(const CompiledData::Binding *binding) const
{
return m_compilationUnit->bindingValueAsScriptString(binding);
}
struct TranslationDataIndex
{
uint index;
bool byId;
};
QString translateFrom(TranslationDataIndex index) const;
Heap::Module *module() const;
void setModule(Heap::Module *module);
ReturnedValue value() const { return m_valueOrModule.asReturnedValue(); }
// Non-ES script values are held in the context's importedScripts array.
// That one uses the generic write barrier we have for JavaScript arrays.
// We don't need to markCustom() here.
void setValue(const QV4::Value &value) { m_valueOrModule = value; }
const CompiledData::Unit *unitData() const { return m_compilationUnit->data; }
QString stringAt(uint index) const { return m_compilationUnit->stringAt(index); }
const QList<QQmlRefPointer<QQmlScriptData>> *dependentScriptsPtr() const
{
return &m_compilationUnit->dependentScripts;
}
const CompiledData::BindingPropertyData *bindingPropertyDataPerObjectAt(
qsizetype objectIndex) const
{
return &m_compilationUnit->bindingPropertyDataPerObject.at(objectIndex);
}
const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &baseCompilationUnit() const
{
return m_compilationUnit;
}
QV4::Function *rootFunction()
{
if (!runtimeStrings)
populate();
const auto *data = unitData();
return data->indexOfRootFunction != -1
? runtimeFunctions[data->indexOfRootFunction]
: nullptr;
}
template<typename Engine = ExecutionEngine>
QQmlRefPointer<ExecutableCompilationUnit> dependentModule(const QUrl &relative) const
{
auto cu = static_cast<Engine *>(engine)->moduleForUrl(relative, this);
// Make sure that this is actually a dependent script.
// Otherwise we cannot guarantee that it's available.
Q_ASSERT(cu);
Q_ASSERT(std::any_of(m_compilationUnit->dependentScripts.cbegin(),
m_compilationUnit->dependentScripts.cend(),
[&](const auto &dependentScript) {
return dependentScript->url == cu->finalUrl();
}));
return cu;
}
void populate();
void clear();
protected:
quint32 totalStringCount() const
{ return unitData()->stringTableSize; }
private:
friend struct ExecutionEngine;
QQmlRefPointer<CompiledData::CompilationUnit> m_compilationUnit;
Value m_valueOrModule = QV4::Value::emptyValue();
struct ResolveSetEntry
{
ResolveSetEntry() {}
ResolveSetEntry(const ExecutableCompilationUnit *module, QV4::String *exportName)
: module(module), exportName(exportName) {}
const ExecutableCompilationUnit *module = nullptr;
QV4::String *exportName = nullptr;
};
ExecutableCompilationUnit();
ExecutableCompilationUnit(QQmlRefPointer<CompiledData::CompilationUnit> &&compilationUnit);
~ExecutableCompilationUnit();
static QQmlRefPointer<ExecutableCompilationUnit> create(
QQmlRefPointer<CompiledData::CompilationUnit> &&compilationUnit,
ExecutionEngine *engine);
const Value *resolveExportRecursively(QV4::String *exportName,
QList<ResolveSetEntry> *resolveSet) const;
QUrl urlAt(int index) const { return QUrl(stringAt(index)); }
Q_NEVER_INLINE IdentifierHash createNamedObjectsPerComponent(int componentObjectIndex);
const CompiledData::ExportEntry *lookupNameInExportTable(
const CompiledData::ExportEntry *firstExportEntry, int tableSize,
QV4::String *name) const;
void getExportedNamesRecursively(
QStringList *names, QList<const ExecutableCompilationUnit *> *exportNameSet,
bool includeDefaultExport = true) const;
};
IdentifierHash ExecutableCompilationUnit::namedObjectsPerComponent(int componentObjectIndex)
{
auto it = namedObjectsPerComponentCache.constFind(componentObjectIndex);
if (Q_UNLIKELY(it == namedObjectsPerComponentCache.cend()))
return createNamedObjectsPerComponent(componentObjectIndex);
Q_ASSERT(it->isValid());
return *it;
}
} // namespace QV4
QT_END_NAMESPACE
#endif // QV4EXECUTABLECOMPILATIONUNIT_P_H