aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmldom/qqmldomtypesreader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmldom/qqmldomtypesreader.cpp')
-rw-r--r--src/qmldom/qqmldomtypesreader.cpp272
1 files changed, 272 insertions, 0 deletions
diff --git a/src/qmldom/qqmldomtypesreader.cpp b/src/qmldom/qqmldomtypesreader.cpp
new file mode 100644
index 0000000000..ae1ec7fb94
--- /dev/null
+++ b/src/qmldom/qqmldomtypesreader.cpp
@@ -0,0 +1,272 @@
+// 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 "qqmldomtypesreader_p.h"
+#include "qqmldomelements_p.h"
+#include "qqmldomcompare_p.h"
+#include "qqmldomfieldfilter_p.h"
+
+#include <QtQml/private/qqmljsparser_p.h>
+#include <QtQml/private/qqmljslexer_p.h>
+#include <QtQml/private/qqmljsengine_p.h>
+#include <private/qqmljstypedescriptionreader_p.h>
+
+#include <QtCore/qdir.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+using namespace QQmlJS::AST;
+
+static ErrorGroups readerParseErrors()
+{
+ static ErrorGroups errs = { { NewErrorGroup("Dom"), NewErrorGroup("QmltypesFile"),
+ NewErrorGroup("Parsing") } };
+ return errs;
+}
+
+void QmltypesReader::insertProperty(
+ const QQmlJSScope::ConstPtr &jsScope, const QQmlJSMetaProperty &property,
+ QMap<int, QmlObject> &objs)
+{
+ PropertyDefinition prop;
+ prop.name = property.propertyName();
+ prop.typeName = property.typeName();
+ prop.isPointer = property.isPointer();
+ prop.isReadonly = !property.isWritable();
+ prop.isRequired = jsScope->isPropertyLocallyRequired(prop.name);
+ prop.isList = property.isList();
+ int revision = property.revision();
+ prop.isFinal = property.isFinal();
+ prop.bindable = property.bindable();
+ prop.read = property.read();
+ prop.write = property.write();
+ prop.notify = property.notify();
+
+ if (prop.name.isEmpty() || prop.typeName.isEmpty()) {
+ addError(readerParseErrors()
+ .warning(tr("Property object is missing a name or type script binding."))
+ .handle());
+ return;
+ }
+ objs[revision].addPropertyDef(prop, AddOption::KeepExisting);
+}
+
+void QmltypesReader::insertSignalOrMethod(const QQmlJSMetaMethod &metaMethod,
+ QMap<int, QmlObject> &objs)
+{
+ MethodInfo methodInfo;
+ // ### confusion between Method and Slot. Method should be removed.
+ switch (metaMethod.methodType()) {
+ case QQmlJSMetaMethodType::Method:
+ case QQmlJSMetaMethodType::Slot:
+ methodInfo.methodType = MethodInfo::MethodType::Method;
+ break;
+ case QQmlJSMetaMethodType::Signal:
+ methodInfo.methodType = MethodInfo::MethodType::Signal;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ auto parameters = metaMethod.parameters();
+ qsizetype nParam = parameters.size();
+ for (int i = 0; i < nParam; ++i) {
+ MethodParameter param;
+ param.name = parameters[i].name();
+ param.typeName = parameters[i].typeName();
+ methodInfo.parameters.append(param);
+ }
+ methodInfo.name = metaMethod.methodName();
+ methodInfo.typeName = metaMethod.returnTypeName();
+ int revision = metaMethod.revision();
+ methodInfo.isConstructor = metaMethod.isConstructor();
+ if (methodInfo.name.isEmpty()) {
+ addError(readerParseErrors().error(tr("Method or signal is missing a name.")).handle());
+ return;
+ }
+
+ objs[revision].addMethod(methodInfo, AddOption::KeepExisting);
+}
+
+EnumDecl QmltypesReader::enumFromMetaEnum(const QQmlJSMetaEnum &metaEnum)
+{
+ EnumDecl res;
+ res.setName(metaEnum.name());
+ res.setAlias(metaEnum.alias());
+ res.setIsFlag(metaEnum.isFlag());
+ QList<EnumItem> values;
+ int lastValue = -1;
+ for (const auto &k : metaEnum.keys()) {
+ if (metaEnum.hasValues())
+ lastValue = metaEnum.value(k);
+ else
+ ++lastValue;
+ values.append(EnumItem(k, lastValue));
+ }
+ res.setValues(values);
+ return res;
+}
+
+void QmltypesReader::insertComponent(const QQmlJSScope::ConstPtr &jsScope,
+ const QList<QQmlJSScope::Export> &exportsList)
+{
+ QmltypesComponent comp;
+ comp.setSemanticScope(jsScope);
+ QMap<int, QmlObject> objects;
+ {
+ bool hasExports = false;
+ for (const QQmlJSScope::Export &jsE : exportsList) {
+ int metaRev = jsE.version().toEncodedVersion<int>();
+ hasExports = true;
+ QmlObject object;
+ object.setSemanticScope(jsScope);
+ objects.insert(metaRev, object);
+ }
+ if (!hasExports) {
+ QmlObject object;
+ object.setSemanticScope(jsScope);
+ objects.insert(0, object);
+ }
+ }
+ bool incrementedPath = false;
+ QString prototype;
+ QString defaultPropertyName;
+ {
+ QHash<QString, QQmlJSMetaProperty> els = jsScope->ownProperties();
+ auto it = els.cbegin();
+ auto end = els.cend();
+ while (it != end) {
+ insertProperty(jsScope, it.value(), objects);
+ ++it;
+ }
+ }
+ {
+ QMultiHash<QString, QQmlJSMetaMethod> els = jsScope->ownMethods();
+ auto it = els.cbegin();
+ auto end = els.cend();
+ while (it != end) {
+ insertSignalOrMethod(it.value(), objects);
+ ++it;
+ }
+ }
+ {
+ QHash<QString, QQmlJSMetaEnum> els = jsScope->ownEnumerations();
+ auto it = els.cbegin();
+ auto end = els.cend();
+ while (it != end) {
+ comp.addEnumeration(enumFromMetaEnum(it.value()));
+ ++it;
+ }
+ }
+ comp.setFileName(jsScope->filePath());
+ comp.setName(jsScope->internalName());
+ m_currentPath = m_currentPath.key(comp.name())
+ .index(qmltypesFilePtr()->components().values(comp.name()).size());
+ incrementedPath = true;
+ prototype = jsScope->baseTypeName();
+ defaultPropertyName = jsScope->ownDefaultPropertyName();
+ comp.setInterfaceNames(jsScope->interfaceNames());
+ QString typeName = jsScope->ownAttachedTypeName();
+ comp.setAttachedTypeName(typeName);
+ if (!typeName.isEmpty())
+ comp.setAttachedTypePath(Paths::lookupCppTypePath(typeName));
+ comp.setIsSingleton(jsScope->isSingleton());
+ comp.setIsCreatable(jsScope->isCreatable());
+ comp.setIsComposite(jsScope->isComposite());
+ comp.setHasCustomParser(jsScope->hasCustomParser());
+ comp.setValueTypeName(jsScope->valueTypeName());
+ comp.setAccessSemantics(jsScope->accessSemantics());
+ comp.setExtensionTypeName(jsScope->extensionTypeName());
+ comp.setExtensionIsJavaScript(jsScope->extensionIsJavaScript());
+ comp.setExtensionIsNamespace(jsScope->extensionIsNamespace());
+ Path exportSourcePath = qmltypesFile().canonicalPath();
+ QMap<int, Path> revToPath;
+ auto it = objects.end();
+ auto begin = objects.begin();
+ int objectIndex = 0;
+ QList<int> metaRevs;
+ Path compPath = qmltypesFile()
+ .canonicalPath()
+ .field(Fields::components)
+ .key(comp.name())
+ .index(qmltypesFilePtr()->components().values(comp.name()).size());
+
+ // emit & map objs
+ while (it != begin) {
+ --it;
+ if (it.key() < 0) {
+ addError(readerParseErrors().error(
+ tr("negative meta revision %1 not supported").arg(it.key())));
+ }
+ revToPath.insert(it.key(), compPath.field(Fields::objects).index(objectIndex));
+ Path nextObjectPath = compPath.field(Fields::objects).index(++objectIndex);
+ if (it == begin) {
+ if (!prototype.isEmpty())
+ it->addPrototypePath(Paths::lookupCppTypePath(prototype));
+ it->setName(prototype);
+ } else {
+ it->addPrototypePath(nextObjectPath);
+ it->setName(comp.name() + QLatin1String("-") + QString::number(it.key()));
+ }
+ comp.addObject(*it);
+ metaRevs.append(it.key());
+ }
+ comp.setMetaRevisions(metaRevs);
+
+ // exports:
+ QList<Export> exports;
+ for (const QQmlJSScope::Export &jsE : exportsList) {
+ auto v = jsE.version();
+ int metaRev = v.toEncodedVersion<int>();
+ Export e;
+ e.uri = jsE.package();
+ e.typeName = jsE.type();
+ e.isSingleton = jsScope->isSingleton();
+ e.version = Version((v.hasMajorVersion() ? v.majorVersion() : Version::Latest),
+ (v.hasMinorVersion() ? v.minorVersion() : Version::Latest));
+ e.typePath = revToPath.value(metaRev);
+ if (!e.typePath) {
+ qCWarning(domLog) << "could not find version" << metaRev << "in" << revToPath.keys();
+ }
+ e.exportSourcePath = exportSourcePath;
+ comp.addExport(e);
+ }
+
+ if (comp.name().isEmpty()) {
+ addError(readerParseErrors()
+ .error(tr("Component definition is missing a name binding."))
+ .handle());
+ return;
+ }
+ qmltypesFilePtr()->addComponent(comp, AddOption::KeepExisting);
+ if (incrementedPath)
+ m_currentPath = m_currentPath.dropTail().dropTail();
+}
+
+bool QmltypesReader::parse()
+{
+ QQmlJSTypeDescriptionReader reader(qmltypesFilePtr()->canonicalFilePath(),
+ qmltypesFilePtr()->code());
+ QStringList dependencies;
+ QList<QQmlJSExportedScope> objects;
+ const bool isValid = reader(&objects, &dependencies);
+ for (const auto &obj : std::as_const(objects))
+ insertComponent(obj.scope, obj.exports);
+ qmltypesFilePtr()->setIsValid(isValid);
+ return isValid;
+}
+
+void QmltypesReader::addError(ErrorMessage &&message)
+{
+ if (message.file.isEmpty())
+ message.file = qmltypesFile().canonicalFilePath();
+ if (!message.path)
+ message.path = m_currentPath;
+ qmltypesFilePtr()->addErrorLocal(message.handle());
+}
+
+} // end namespace Dom
+} // end namespace QQmlJS
+QT_END_NAMESPACE