[new compiler] Implement proper type resolution

Collect all references to unknown types after parsing, re-use the existing code
in QQmlTypeLoader to resolve them and finally use the resolved references map
in the QQmlObjectCreator instead of the type name cache directly.

Change-Id: I8b83af4f8852e79c33985457081c024358bb9622
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
This commit is contained in:
Simon Hausmann 2013-09-12 10:53:20 +02:00 committed by The Qt Project
parent 8c2201e2e5
commit 4b5a7b15fc
10 changed files with 162 additions and 19 deletions

View File

@ -109,6 +109,7 @@ bool QQmlCodeGenerator::generateFromQml(const QString &code, const QUrl &url, co
qSwap(_imports, output->imports);
qSwap(_objects, output->objects);
qSwap(_functions, output->functions);
qSwap(_typeReferences, output->typeReferences);
this->pool = output->jsParserEngine.pool();
this->jsGenerator = &output->jsGenerator;
@ -128,9 +129,13 @@ bool QQmlCodeGenerator::generateFromQml(const QString &code, const QUrl &url, co
AST::UiObjectDefinition *rootObject = AST::cast<AST::UiObjectDefinition*>(program->members->member);
Q_ASSERT(rootObject);
output->indexOfRootObject = defineQMLObject(rootObject);
collectTypeReferences();
qSwap(_imports, output->imports);
qSwap(_objects, output->objects);
qSwap(_functions, output->functions);
qSwap(_typeReferences, output->typeReferences);
return true;
}
@ -440,6 +445,8 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node)
}
param->nameIndex = registerString(p->name.toString());
param->location.line = p->identifierToken.startLine;
param->location.column = p->identifierToken.startColumn;
signal->parameters->append(param);
p = p->next;
}
@ -710,6 +717,22 @@ void QQmlCodeGenerator::recordError(const AST::SourceLocation &location, const Q
errors << error;
}
void QQmlCodeGenerator::collectTypeReferences()
{
foreach (QmlObject *obj, _objects) {
_typeReferences.add(obj->inheritedTypeNameIndex, obj->location);
for (QmlProperty *prop = obj->properties->first; prop; prop = prop->next) {
if (prop->type >= QV4::CompiledData::Property::Custom)
_typeReferences.add(prop->customTypeNameIndex, prop->location);
}
for (Signal *sig = obj->qmlSignals->first; sig; sig = sig->next)
for (SignalParameter *param = sig->parameters->first; param; param = param->next)
_typeReferences.add(param->customTypeNameIndex, param->location);
}
}
QQmlScript::LocationSpan QQmlCodeGenerator::location(AST::SourceLocation start, AST::SourceLocation end)
{
QQmlScript::LocationSpan rv;

View File

@ -163,6 +163,8 @@ struct ParsedQML
QList<AST::Node*> functions; // FunctionDeclaration, Statement or Expression
QV4::Compiler::JSUnitGenerator jsGenerator;
QV4::CompiledData::TypeReferenceMap typeReferences;
QString stringAt(int index) const { return jsGenerator.strings.value(index); }
};
@ -220,6 +222,8 @@ public:
void recordError(const AST::SourceLocation &location, const QString &description);
void collectTypeReferences();
static QQmlScript::LocationSpan location(AST::SourceLocation start, AST::SourceLocation end);
int registerString(const QString &str) const { return jsGenerator->registerString(str); }
@ -231,6 +235,8 @@ public:
QList<QmlObject*> _objects;
QList<AST::Node*> _functions;
QV4::CompiledData::TypeReferenceMap _typeReferences;
QmlObject *_object;
QSet<QString> _propertyNames;
QSet<QString> _signalNames;

View File

@ -44,6 +44,7 @@
#include <QtCore/qstring.h>
#include <QVector>
#include <QStringList>
#include <QHash>
#include <private/qv4value_def_p.h>
#include <private/qv4executableallocator_p.h>
@ -73,6 +74,16 @@ struct Location
int column;
};
// map from name index to location of first use
struct TypeReferenceMap : QHash<int, Location>
{
void add(int nameIndex, const Location &loc) {
if (contains(nameIndex))
return;
insert(nameIndex, loc);
}
};
struct RegExp
{
enum Flags {
@ -263,6 +274,7 @@ struct Parameter
quint32 type;
quint32 customTypeNameIndex;
quint32 reserved;
Location location;
};
struct Signal

View File

@ -116,6 +116,14 @@ QQmlCompiledData::~QQmlCompiledData()
types.at(ii).typePropertyCache->release();
}
for (QHash<int, TypeReference>::Iterator resolvedType = resolvedTypes.begin(), end = resolvedTypes.end();
resolvedType != end; ++resolvedType) {
if (resolvedType->component)
resolvedType->component->release();
if (resolvedType->typePropertyCache)
resolvedType->typePropertyCache->release();
}
for (int ii = 0; ii < propertyCaches.count(); ++ii)
propertyCaches.at(ii)->release();

View File

@ -110,7 +110,12 @@ public:
QQmlPropertyCache *propertyCache() const;
QQmlPropertyCache *createPropertyCache(QQmlEngine *);
};
// --- old compiler:
QList<TypeReference> types;
// --- new compiler:
// map from name index
QHash<int, TypeReference> resolvedTypes;
// ---
struct V8Program {
V8Program(const QByteArray &p, QQmlCompiledData *c)

View File

@ -900,7 +900,7 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context)
if (cc->compilationUnit && !cc->compilationUnit->engine)
cc->compilationUnit->linkToEngine(v4);
state.creator = new QmlObjectCreator(context, cc->qmlUnit, cc->compilationUnit, cc->importCache,
state.creator = new QmlObjectCreator(context, cc->qmlUnit, cc->compilationUnit, cc->resolvedTypes,
cc->propertyCaches, cc->datas);
rv = state.creator->create();
if (!rv)

View File

@ -60,21 +60,22 @@ QT_USE_NAMESPACE
static QAtomicInt classIndexCounter(0);
QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlEnginePrivate *enginePrivate, const QV4::CompiledData::QmlUnit *unit, const QUrl &url, QQmlTypeNameCache *typeNameCache, const QQmlImports *imports)
QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlEnginePrivate *enginePrivate, const QV4::CompiledData::QmlUnit *unit, const QUrl &url, const QQmlImports *imports,
QHash<int, QQmlCompiledData::TypeReference> *resolvedTypes)
: enginePrivate(enginePrivate)
, unit(unit)
, url(url)
, typeNameCache(typeNameCache)
, imports(imports)
, resolvedTypes(resolvedTypes)
{
}
bool QQmlPropertyCacheCreator::create(const QV4::CompiledData::Object *obj, QQmlPropertyCache **resultCache, QByteArray *vmeMetaObjectData)
{
QQmlTypeNameCache::Result res = typeNameCache->query(stringAt(obj->inheritedTypeNameIndex));
Q_ASSERT(res.isValid()); // types resolved earlier in resolveTypes()
QQmlType *baseType = resolvedTypes->value(obj->inheritedTypeNameIndex).type;
Q_ASSERT(baseType);
QQmlPropertyCache *baseTypeCache = enginePrivate->cache(res.type->metaObject());
QQmlPropertyCache *baseTypeCache = enginePrivate->cache(baseType->metaObject());
if (obj->nProperties == 0 && obj->nSignals == 0 && obj->nFunctions == 0) {
*resultCache = baseTypeCache;
vmeMetaObjectData->clear();
@ -441,14 +442,15 @@ static void removeBindingOnProperty(QObject *o, int index)
}
QmlObjectCreator::QmlObjectCreator(QQmlContextData *contextData, const QV4::CompiledData::QmlUnit *qmlUnit,
const QV4::CompiledData::CompilationUnit *jsUnit, QQmlTypeNameCache *typeNameCache,
const QV4::CompiledData::CompilationUnit *jsUnit,
const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes,
const QList<QQmlPropertyCache*> &propertyCaches,
const QList<QByteArray> &vmeMetaObjectData)
: engine(contextData->engine)
, unit(qmlUnit)
, jsUnit(jsUnit)
, context(contextData)
, typeNameCache(typeNameCache)
, resolvedTypes(resolvedTypes)
, propertyCaches(propertyCaches)
, vmeMetaObjectData(vmeMetaObjectData)
, _qobject(0)
@ -546,11 +548,10 @@ QObject *QmlObjectCreator::create(int index, QObject *parent)
{
const QV4::CompiledData::Object *obj = unit->objectAt(index);
QQmlTypeNameCache::Result res = typeNameCache->query(stringAt(obj->inheritedTypeNameIndex));
if (!res.isValid())
return 0;
QQmlType *type = resolvedTypes.value(obj->inheritedTypeNameIndex).type;
Q_ASSERT(type);
QObject *result = res.type->create();
QObject *result = type->create();
// ### use no-event variant
if (parent)
result->setParent(parent);

View File

@ -44,6 +44,7 @@
#include <private/qqmlimport_p.h>
#include <private/qqmltypenamecache_p.h>
#include <private/qv4compileddata_p.h>
#include <private/qqmlcompiler_p.h>
QT_BEGIN_NAMESPACE
@ -54,7 +55,8 @@ class QQmlPropertyCacheCreator
Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreator)
public:
QQmlPropertyCacheCreator(QQmlEnginePrivate *enginePrivate, const QV4::CompiledData::QmlUnit *unit,
const QUrl &url, QQmlTypeNameCache *typeNameCache, const QQmlImports *imports);
const QUrl &url, const QQmlImports *imports,
QHash<int, QQmlCompiledData::TypeReference> *resolvedTypes);
QList<QQmlError> errors;
@ -67,8 +69,8 @@ protected:
QQmlEnginePrivate *enginePrivate;
const QV4::CompiledData::QmlUnit *unit;
QUrl url;
QQmlTypeNameCache *typeNameCache;
const QQmlImports *imports;
QHash<int, QQmlCompiledData::TypeReference> *resolvedTypes;
};
class QmlObjectCreator
@ -76,7 +78,7 @@ class QmlObjectCreator
Q_DECLARE_TR_FUNCTIONS(QmlObjectCreator)
public:
QmlObjectCreator(QQmlContextData *contextData, const QV4::CompiledData::QmlUnit *qmlUnit, const QV4::CompiledData::CompilationUnit *jsUnit,
QQmlTypeNameCache *typeNameCache, const QList<QQmlPropertyCache *> &propertyCaches, const QList<QByteArray> &vmeMetaObjectData);
const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes, const QList<QQmlPropertyCache *> &propertyCaches, const QList<QByteArray> &vmeMetaObjectData);
QObject *create(QObject *parent = 0)
{ return create(unit->indexOfRootObject, parent); }
@ -102,7 +104,7 @@ private:
const QV4::CompiledData::QmlUnit *unit;
const QV4::CompiledData::CompilationUnit *jsUnit;
QQmlContextData *context;
QQmlTypeNameCache *typeNameCache;
const QHash<int, QQmlCompiledData::TypeReference> resolvedTypes;
const QList<QQmlPropertyCache *> propertyCaches;
const QList<QByteArray> vmeMetaObjectData;

View File

@ -2167,6 +2167,13 @@ void QQmlTypeData::compile()
m_imports.populateCache(m_compiledData->importCache);
m_compiledData->importCache->addref();
for (QHash<int, TypeReference>::ConstIterator resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd();
resolvedType != end; ++resolvedType) {
QQmlCompiledData::TypeReference ref;
ref.type = resolvedType->type;
m_compiledData->resolvedTypes.insert(resolvedType.key(), ref);
}
JSCodeGen jsCodeGen;
jsCodeGen.generateJSCodeForFunctionsAndBindings(finalUrlString(), parsedQML.data());
@ -2196,8 +2203,8 @@ void QQmlTypeData::compile()
m_compiledData->propertyCaches.reserve(qmlUnit->nObjects);
QQmlPropertyCacheCreator propertyCacheBuilder(QQmlEnginePrivate::get(m_typeLoader->engine()),
qmlUnit, m_compiledData->url, m_compiledData->importCache,
&m_imports);
qmlUnit, m_compiledData->url,
&m_imports, &m_compiledData->resolvedTypes);
for (quint32 i = 0; i < qmlUnit->nObjects; ++i) {
const QV4::CompiledData::Object *obj = qmlUnit->objectAt(i);
@ -2255,10 +2262,10 @@ void QQmlTypeData::resolveTypes()
m_scripts << ref;
}
// --- old compiler:
foreach (QQmlScript::TypeReference *parserRef, scriptParser.referencedTypes()) {
TypeReference ref;
QString url;
int majorVersion = -1;
int minorVersion = -1;
QQmlImportNamespace *typeNamespace = 0;
@ -2318,6 +2325,80 @@ void QQmlTypeData::resolveTypes()
m_types << ref;
}
// --- new compiler:
QV4::CompiledData::TypeReferenceMap typeReferences;
QStringList names;
if (parsedQML) {
typeReferences = parsedQML->typeReferences;
names = parsedQML->jsGenerator.strings;
} else {
// ### collect from available QV4::CompiledData::QmlUnit
}
for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = typeReferences.constBegin(), end = typeReferences.constEnd();
unresolvedRef != end; ++unresolvedRef) {
TypeReference ref; // resolved reference
int majorVersion = -1;
int minorVersion = -1;
QQmlImportNamespace *typeNamespace = 0;
QList<QQmlError> errors;
const QString name = names.at(unresolvedRef.key());
bool typeFound = m_imports.resolveType(name, &ref.type,
&majorVersion, &minorVersion, &typeNamespace, &errors);
if (!typeNamespace && !typeFound && !m_implicitImportLoaded) {
// Lazy loading of implicit import
if (loadImplicitImport()) {
// Try again to find the type
errors.clear();
typeFound = m_imports.resolveType(name, &ref.type,
&majorVersion, &minorVersion, &typeNamespace, &errors);
} else {
return; //loadImplicitImport() hit an error, and called setError already
}
}
if (!typeFound || typeNamespace) {
// Known to not be a type:
// - known to be a namespace (Namespace {})
// - type with unknown namespace (UnknownNamespace.SomeType {})
QQmlError error;
if (typeNamespace) {
error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(name));
} else {
if (errors.size()) {
error = errors.takeFirst();
} else {
// this should not be possible!
// Description should come from error provided by addImport() function.
error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database"));
}
error.setUrl(m_imports.baseUrl());
error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(name).arg(error.description()));
}
error.setLine(unresolvedRef->line);
error.setColumn(unresolvedRef->column);
errors.prepend(error);
setError(errors);
return;
}
if (ref.type->isComposite()) {
ref.typeData = typeLoader()->getType(ref.type->sourceUrl());
addDependency(ref.typeData);
}
ref.majorVersion = majorVersion;
ref.minorVersion = minorVersion;
ref.location.line = unresolvedRef->line;
ref.location.column = unresolvedRef->column;
m_resolvedTypes.insert(unresolvedRef.key(), ref);
}
}
void QQmlTypeData::scriptImported(QQmlScriptBlob *blob, const QQmlScript::Location &location, const QString &qualifier, const QString &/*nameSpace*/)

View File

@ -457,7 +457,12 @@ private:
QSet<QString> m_namespaces;
// --- old compiler
QList<TypeReference> m_types;
// --- new compiler
// map from name index to resolved type
QHash<int, TypeReference> m_resolvedTypes;
// ---
bool m_typesResolved:1;
bool m_useNewCompiler:1;