/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #include "qmljsinterpreter.h" #include "qmljsevaluate.h" #include "qmljsscopebuilder.h" #include "qmljsscopechain.h" #include "qmljsscopeastpath.h" #include "qmljstypedescriptionreader.h" #include "qmljsvalueowner.h" #include "qmljscontext.h" #include "qmljsmodelmanagerinterface.h" #include "parser/qmljsast_p.h" #include #include #include #include #include #include #include using namespace LanguageUtils; using namespace QmlJS; using namespace QmlJS::AST; /*! \class QmlJS::Value \brief The Value class is an abstract base class for the result of a JS expression. \sa Evaluate ValueOwner ValueVisitor A Value represents a category of JavaScript values, such as number (NumberValue), string (StringValue) or functions with a specific signature (FunctionValue). It can also represent internal categories such as "a QML component instantiation defined in a file" (ASTObjectValue), "a QML component defined in C++" (CppComponentValue) or "no specific information is available" (UnknownValue). The Value class itself provides accept() for admitting \l{ValueVisitor}s and a do-nothing getSourceLocation(). Value instances should be cast to a derived type either through the asXXX() helper functions such as asNumberValue() or via the value_cast() template function. Values are the result of many operations in the QmlJS code model: \list \li \l{Evaluate} \li Context::lookupType() and Context::lookupReference() \li ScopeChain::lookup() \li ObjectValue::lookupMember() \endlist */ namespace { class LookupMember: public MemberProcessor { QString _name; const Value *_value; bool process(const QString &name, const Value *value) { if (_value) return false; if (name == _name) { _value = value; return false; } return true; } public: LookupMember(const QString &name) : _name(name), _value(0) {} const Value *value() const { return _value; } virtual bool processProperty(const QString &name, const Value *value) { return process(name, value); } virtual bool processEnumerator(const QString &name, const Value *value) { return process(name, value); } virtual bool processSignal(const QString &name, const Value *value) { return process(name, value); } virtual bool processSlot(const QString &name, const Value *value) { return process(name, value); } virtual bool processGeneratedSlot(const QString &name, const Value *value) { return process(name, value); } }; class MetaFunction: public FunctionValue { FakeMetaMethod _method; public: MetaFunction(const FakeMetaMethod &method, ValueOwner *valueOwner) : FunctionValue(valueOwner), _method(method) { } virtual int namedArgumentCount() const { return _method.parameterNames().size(); } virtual QString argumentName(int index) const { if (index < _method.parameterNames().size()) return _method.parameterNames().at(index); return FunctionValue::argumentName(index); } virtual bool isVariadic() const { return false; } }; } // end of anonymous namespace CppComponentValue::CppComponentValue(FakeMetaObject::ConstPtr metaObject, const QString &className, const QString &packageName, const ComponentVersion &componentVersion, const ComponentVersion &importVersion, int metaObjectRevision, ValueOwner *valueOwner) : ObjectValue(valueOwner), _metaObject(metaObject), _moduleName(packageName), _componentVersion(componentVersion), _importVersion(importVersion), _metaObjectRevision(metaObjectRevision) { setClassName(className); int nEnums = metaObject->enumeratorCount(); for (int i = 0; i < nEnums; ++i) { FakeMetaEnum fEnum = metaObject->enumerator(i); _enums[fEnum.name()] = new QmlEnumValue(this, i); } } CppComponentValue::~CppComponentValue() { #if QT_VERSION >= 0x050000 delete _metaSignatures.load(); delete _signalScopes.load(); #else delete _metaSignatures; delete _signalScopes; #endif } static QString generatedSlotName(const QString &base) { QString slotName = QLatin1String("on"); slotName += base.at(0).toUpper(); slotName += base.midRef(1); return slotName; } const CppComponentValue *CppComponentValue::asCppComponentValue() const { return this; } void CppComponentValue::processMembers(MemberProcessor *processor) const { // process the meta enums for (int index = _metaObject->enumeratorOffset(); index < _metaObject->enumeratorCount(); ++index) { FakeMetaEnum e = _metaObject->enumerator(index); for (int i = 0; i < e.keyCount(); ++i) { processor->processEnumerator(e.key(i), valueOwner()->numberValue()); } } // all explicitly defined signal names QSet explicitSignals; // make MetaFunction instances lazily when first needed #if QT_VERSION >= 0x050000 QList *signatures = _metaSignatures.load(); #else QList *signatures = _metaSignatures; #endif if (!signatures) { signatures = new QList; signatures->reserve(_metaObject->methodCount()); for (int index = 0; index < _metaObject->methodCount(); ++index) signatures->append(new MetaFunction(_metaObject->method(index), valueOwner())); if (!_metaSignatures.testAndSetOrdered(0, signatures)) { delete signatures; #if QT_VERSION >= 0x050000 signatures = _metaSignatures.load(); #else signatures = _metaSignatures; #endif } } // process the meta methods for (int index = 0; index < _metaObject->methodCount(); ++index) { const FakeMetaMethod method = _metaObject->method(index); if (_metaObjectRevision < method.revision()) continue; const QString &methodName = _metaObject->method(index).methodName(); const Value *signature = signatures->at(index); if (method.methodType() == FakeMetaMethod::Slot && method.access() == FakeMetaMethod::Public) { processor->processSlot(methodName, signature); } else if (method.methodType() == FakeMetaMethod::Signal && method.access() != FakeMetaMethod::Private) { // process the signal processor->processSignal(methodName, signature); explicitSignals.insert(methodName); // process the generated slot const QString &slotName = generatedSlotName(methodName); processor->processGeneratedSlot(slotName, signature); } } // process the meta properties for (int index = 0; index < _metaObject->propertyCount(); ++index) { const FakeMetaProperty prop = _metaObject->property(index); if (_metaObjectRevision < prop.revision()) continue; const QString propertyName = prop.name(); processor->processProperty(propertyName, valueForCppName(prop.typeName())); // every property always has a onXyzChanged slot, even if the NOTIFY // signal has a different name QString signalName = propertyName; signalName += QLatin1String("Changed"); if (!explicitSignals.contains(signalName)) { // process the generated slot const QString &slotName = generatedSlotName(signalName); processor->processGeneratedSlot(slotName, valueOwner()->unknownValue()); } } // look into attached types const QString &attachedTypeName = _metaObject->attachedTypeName(); if (!attachedTypeName.isEmpty()) { const CppComponentValue *attachedType = valueOwner()->cppQmlTypes().objectByCppName(attachedTypeName); if (attachedType && attachedType != this) // ### only weak protection against infinite loops attachedType->processMembers(processor); } ObjectValue::processMembers(processor); } const Value *CppComponentValue::valueForCppName(const QString &typeName) const { const CppQmlTypes &cppTypes = valueOwner()->cppQmlTypes(); // check in the same package/version first const CppComponentValue *objectValue = cppTypes.objectByQualifiedName( _moduleName, typeName, _importVersion); if (objectValue) return objectValue; // fallback to plain cpp name objectValue = cppTypes.objectByCppName(typeName); if (objectValue) return objectValue; // try qml builtin type names if (const Value *v = valueOwner()->defaultValueForBuiltinType(typeName)) { if (!v->asUndefinedValue()) return v; } // map other C++ types if (typeName == QLatin1String("QByteArray") || typeName == QLatin1String("QString")) { return valueOwner()->stringValue(); } else if (typeName == QLatin1String("QUrl")) { return valueOwner()->urlValue(); } else if (typeName == QLatin1String("long")) { return valueOwner()->intValue(); } else if (typeName == QLatin1String("float") || typeName == QLatin1String("qreal")) { return valueOwner()->realValue(); } else if (typeName == QLatin1String("QFont")) { return valueOwner()->qmlFontObject(); } else if (typeName == QLatin1String("QPoint") || typeName == QLatin1String("QPointF") || typeName == QLatin1String("QVector2D")) { return valueOwner()->qmlPointObject(); } else if (typeName == QLatin1String("QSize") || typeName == QLatin1String("QSizeF")) { return valueOwner()->qmlSizeObject(); } else if (typeName == QLatin1String("QRect") || typeName == QLatin1String("QRectF")) { return valueOwner()->qmlRectObject(); } else if (typeName == QLatin1String("QVector3D")) { return valueOwner()->qmlVector3DObject(); } else if (typeName == QLatin1String("QColor")) { return valueOwner()->colorValue(); } else if (typeName == QLatin1String("QDeclarativeAnchorLine")) { return valueOwner()->anchorLineValue(); } // might be an enum const CppComponentValue *base = this; const QStringList components = typeName.split(QLatin1String("::")); if (components.size() == 2) base = valueOwner()->cppQmlTypes().objectByCppName(components.first()); if (base) { if (const QmlEnumValue *value = base->getEnumValue(components.last())) return value; } // may still be a cpp based value return valueOwner()->unknownValue(); } const CppComponentValue *CppComponentValue::prototype() const { Q_ASSERT(!_prototype || value_cast(_prototype)); return static_cast(_prototype); } /*! Returns a list started by this object and followed by all its prototypes. Use this function rather than calling prototype() in a loop, as it avoids cycles. */ QList CppComponentValue::prototypes() const { QList protos; for (const CppComponentValue *it = this; it; it = it->prototype()) { if (protos.contains(it)) break; protos += it; } return protos; } FakeMetaObject::ConstPtr CppComponentValue::metaObject() const { return _metaObject; } QString CppComponentValue::moduleName() const { return _moduleName; } ComponentVersion CppComponentValue::componentVersion() const { return _componentVersion; } ComponentVersion CppComponentValue::importVersion() const { return _importVersion; } QString CppComponentValue::defaultPropertyName() const { return _metaObject->defaultPropertyName(); } QString CppComponentValue::propertyType(const QString &propertyName) const { foreach (const CppComponentValue *it, prototypes()) { FakeMetaObject::ConstPtr iter = it->_metaObject; int propIdx = iter->propertyIndex(propertyName); if (propIdx != -1) return iter->property(propIdx).typeName(); } return QString(); } bool CppComponentValue::isListProperty(const QString &propertyName) const { foreach (const CppComponentValue *it, prototypes()) { FakeMetaObject::ConstPtr iter = it->_metaObject; int propIdx = iter->propertyIndex(propertyName); if (propIdx != -1) return iter->property(propIdx).isList(); } return false; } FakeMetaEnum CppComponentValue::getEnum(const QString &typeName, const CppComponentValue **foundInScope) const { foreach (const CppComponentValue *it, prototypes()) { FakeMetaObject::ConstPtr iter = it->_metaObject; const int index = iter->enumeratorIndex(typeName); if (index != -1) { if (foundInScope) *foundInScope = it; return iter->enumerator(index); } } if (foundInScope) *foundInScope = 0; return FakeMetaEnum(); } const QmlEnumValue *CppComponentValue::getEnumValue(const QString &typeName, const CppComponentValue **foundInScope) const { foreach (const CppComponentValue *it, prototypes()) { if (const QmlEnumValue *e = it->_enums.value(typeName)) { if (foundInScope) *foundInScope = it; return e; } } if (foundInScope) *foundInScope = 0; return 0; } const ObjectValue *CppComponentValue::signalScope(const QString &signalName) const { #if QT_VERSION >= 0x050000 QHash *scopes = _signalScopes.load(); #else QHash *scopes = _signalScopes; #endif if (!scopes) { scopes = new QHash; // usually not all methods are signals scopes->reserve(_metaObject->methodCount() / 2); for (int index = 0; index < _metaObject->methodCount(); ++index) { const FakeMetaMethod &method = _metaObject->method(index); if (method.methodType() != FakeMetaMethod::Signal || method.access() == FakeMetaMethod::Private) continue; const QStringList ¶meterNames = method.parameterNames(); const QStringList ¶meterTypes = method.parameterTypes(); QTC_ASSERT(parameterNames.size() == parameterTypes.size(), continue); ObjectValue *scope = valueOwner()->newObject(/*prototype=*/0); for (int i = 0; i < parameterNames.size(); ++i) { const QString &name = parameterNames.at(i); const QString &type = parameterTypes.at(i); if (name.isEmpty()) continue; scope->setMember(name, valueForCppName(type)); } scopes->insert(generatedSlotName(method.methodName()), scope); } if (!_signalScopes.testAndSetOrdered(0, scopes)) { delete scopes; #if QT_VERSION >= 0x050000 scopes = _signalScopes.load(); #else scopes = _signalScopes; #endif } } return scopes->value(signalName); } bool CppComponentValue::isWritable(const QString &propertyName) const { foreach (const CppComponentValue *it, prototypes()) { FakeMetaObject::ConstPtr iter = it->_metaObject; int propIdx = iter->propertyIndex(propertyName); if (propIdx != -1) return iter->property(propIdx).isWritable(); } return false; } bool CppComponentValue::isPointer(const QString &propertyName) const { foreach (const CppComponentValue *it, prototypes()) { FakeMetaObject::ConstPtr iter = it->_metaObject; int propIdx = iter->propertyIndex(propertyName); if (propIdx != -1) return iter->property(propIdx).isPointer(); } return false; } bool CppComponentValue::hasLocalProperty(const QString &typeName) const { int idx = _metaObject->propertyIndex(typeName); if (idx == -1) return false; return true; } bool CppComponentValue::hasProperty(const QString &propertyName) const { foreach (const CppComponentValue *it, prototypes()) { FakeMetaObject::ConstPtr iter = it->_metaObject; int propIdx = iter->propertyIndex(propertyName); if (propIdx != -1) return true; } return false; } bool CppComponentValue::isDerivedFrom(FakeMetaObject::ConstPtr base) const { foreach (const CppComponentValue *it, prototypes()) { FakeMetaObject::ConstPtr iter = it->_metaObject; if (iter == base) return true; } return false; } QmlEnumValue::QmlEnumValue(const CppComponentValue *owner, int enumIndex) : _owner(owner) , _enumIndex(enumIndex) { owner->valueOwner()->registerValue(this); } QmlEnumValue::~QmlEnumValue() { } const QmlEnumValue *QmlEnumValue::asQmlEnumValue() const { return this; } QString QmlEnumValue::name() const { return _owner->metaObject()->enumerator(_enumIndex).name(); } QStringList QmlEnumValue::keys() const { return _owner->metaObject()->enumerator(_enumIndex).keys(); } const CppComponentValue *QmlEnumValue::owner() const { return _owner; } //////////////////////////////////////////////////////////////////////////////// // ValueVisitor //////////////////////////////////////////////////////////////////////////////// ValueVisitor::ValueVisitor() { } ValueVisitor::~ValueVisitor() { } void ValueVisitor::visit(const NullValue *) { } void ValueVisitor::visit(const UndefinedValue *) { } void ValueVisitor::visit(const UnknownValue *) { } void ValueVisitor::visit(const NumberValue *) { } void ValueVisitor::visit(const BooleanValue *) { } void ValueVisitor::visit(const StringValue *) { } void ValueVisitor::visit(const ObjectValue *) { } void ValueVisitor::visit(const FunctionValue *) { } void ValueVisitor::visit(const Reference *) { } void ValueVisitor::visit(const ColorValue *) { } void ValueVisitor::visit(const AnchorLineValue *) { } //////////////////////////////////////////////////////////////////////////////// // Value //////////////////////////////////////////////////////////////////////////////// Value::Value() { } Value::~Value() { } bool Value::getSourceLocation(QString *, int *, int *) const { return false; } const NullValue *Value::asNullValue() const { return 0; } const UndefinedValue *Value::asUndefinedValue() const { return 0; } const UnknownValue *Value::asUnknownValue() const { return 0; } const NumberValue *Value::asNumberValue() const { return 0; } const IntValue *Value::asIntValue() const { return 0; } const RealValue *Value::asRealValue() const { return 0; } const BooleanValue *Value::asBooleanValue() const { return 0; } const StringValue *Value::asStringValue() const { return 0; } const UrlValue *Value::asUrlValue() const { return 0; } const ObjectValue *Value::asObjectValue() const { return 0; } const FunctionValue *Value::asFunctionValue() const { return 0; } const Reference *Value::asReference() const { return 0; } const ColorValue *Value::asColorValue() const { return 0; } const AnchorLineValue *Value::asAnchorLineValue() const { return 0; } const CppComponentValue *Value::asCppComponentValue() const { return 0; } const ASTObjectValue *Value::asAstObjectValue() const { return 0; } const QmlEnumValue *Value::asQmlEnumValue() const { return 0; } const QmlPrototypeReference *Value::asQmlPrototypeReference() const { return 0; } const ASTPropertyReference *Value::asAstPropertyReference() const { return 0; } const ASTSignal *Value::asAstSignal() const { return 0; } //////////////////////////////////////////////////////////////////////////////// // Values //////////////////////////////////////////////////////////////////////////////// const NullValue *NullValue::asNullValue() const { return this; } void NullValue::accept(ValueVisitor *visitor) const { visitor->visit(this); } const UndefinedValue *UndefinedValue::asUndefinedValue() const { return this; } void UnknownValue::accept(ValueVisitor *visitor) const { visitor->visit(this); } const UnknownValue *UnknownValue::asUnknownValue() const { return this; } void UndefinedValue::accept(ValueVisitor *visitor) const { visitor->visit(this); } const NumberValue *NumberValue::asNumberValue() const { return this; } const RealValue *RealValue::asRealValue() const { return this; } const IntValue *IntValue::asIntValue() const { return this; } void NumberValue::accept(ValueVisitor *visitor) const { visitor->visit(this); } const BooleanValue *BooleanValue::asBooleanValue() const { return this; } void BooleanValue::accept(ValueVisitor *visitor) const { visitor->visit(this); } const StringValue *StringValue::asStringValue() const { return this; } const UrlValue *UrlValue::asUrlValue() const { return this; } void StringValue::accept(ValueVisitor *visitor) const { visitor->visit(this); } Reference::Reference(ValueOwner *valueOwner) : _valueOwner(valueOwner) { _valueOwner->registerValue(this); } Reference::~Reference() { } ValueOwner *Reference::valueOwner() const { return _valueOwner; } const Reference *Reference::asReference() const { return this; } void Reference::accept(ValueVisitor *visitor) const { visitor->visit(this); } const Value *Reference::value(ReferenceContext *) const { return _valueOwner->undefinedValue(); } void ColorValue::accept(ValueVisitor *visitor) const { visitor->visit(this); } const ColorValue *ColorValue::asColorValue() const { return this; } void AnchorLineValue::accept(ValueVisitor *visitor) const { visitor->visit(this); } const AnchorLineValue *AnchorLineValue::asAnchorLineValue() const { return this; } MemberProcessor::MemberProcessor() { } MemberProcessor::~MemberProcessor() { } bool MemberProcessor::processProperty(const QString &, const Value *) { return true; } bool MemberProcessor::processEnumerator(const QString &, const Value *) { return true; } bool MemberProcessor::processSignal(const QString &, const Value *) { return true; } bool MemberProcessor::processSlot(const QString &, const Value *) { return true; } bool MemberProcessor::processGeneratedSlot(const QString &, const Value *) { return true; } ObjectValue::ObjectValue(ValueOwner *valueOwner) : _valueOwner(valueOwner), _prototype(0) { valueOwner->registerValue(this); } ObjectValue::~ObjectValue() { } ValueOwner *ObjectValue::valueOwner() const { return _valueOwner; } QString ObjectValue::className() const { return _className; } void ObjectValue::setClassName(const QString &className) { _className = className; } const Value *ObjectValue::prototype() const { return _prototype; } const ObjectValue *ObjectValue::prototype(const Context *context) const { const ObjectValue *prototypeObject = value_cast(_prototype); if (! prototypeObject) { if (const Reference *prototypeReference = value_cast(_prototype)) prototypeObject = value_cast(context->lookupReference(prototypeReference)); } return prototypeObject; } void ObjectValue::setPrototype(const Value *prototype) { _prototype = prototype; } void ObjectValue::setMember(const QString &name, const Value *value) { _members[name] = value; } void ObjectValue::removeMember(const QString &name) { _members.remove(name); } const ObjectValue *ObjectValue::asObjectValue() const { return this; } void ObjectValue::accept(ValueVisitor *visitor) const { visitor->visit(this); } bool ObjectValue::checkPrototype(const ObjectValue *, QSet *) const { #if 0 const int previousSize = processed->size(); processed->insert(this); if (previousSize != processed->size()) { if (this == proto) return false; if (prototype() && ! prototype()->checkPrototype(proto, processed)) return false; return true; } #endif return false; } void ObjectValue::processMembers(MemberProcessor *processor) const { QHashIterator it(_members); while (it.hasNext()) { it.next(); if (! processor->processProperty(it.key(), it.value())) break; } } const Value *ObjectValue::lookupMember(const QString &name, const Context *context, const ObjectValue **foundInObject, bool examinePrototypes) const { if (const Value *m = _members.value(name)) { if (foundInObject) *foundInObject = this; return m; } else { LookupMember slowLookup(name); processMembers(&slowLookup); if (slowLookup.value()) { if (foundInObject) *foundInObject = this; return slowLookup.value(); } } if (examinePrototypes && context) { PrototypeIterator iter(this, context); iter.next(); // skip this while (iter.hasNext()) { const ObjectValue *prototypeObject = iter.next(); if (const Value *m = prototypeObject->lookupMember(name, context, foundInObject, false)) return m; } } if (foundInObject) *foundInObject = 0; return 0; } PrototypeIterator::PrototypeIterator(const ObjectValue *start, const Context *context) : m_current(0) , m_next(start) , m_context(context) , m_error(NoError) { if (start) m_prototypes.reserve(10); } PrototypeIterator::PrototypeIterator(const ObjectValue *start, const ContextPtr &context) : m_current(0) , m_next(start) , m_context(context.data()) , m_error(NoError) { if (start) m_prototypes.reserve(10); } bool PrototypeIterator::hasNext() { if (m_next) return true; if (!m_current) return false; const Value *proto = m_current->prototype(); if (!proto) return false; m_next = value_cast(proto); if (! m_next) m_next = value_cast(m_context->lookupReference(proto)); if (!m_next) { m_error = ReferenceResolutionError; return false; } if (m_prototypes.contains(m_next)) { m_error = CycleError; m_next = 0; return false; } return true; } const ObjectValue *PrototypeIterator::next() { if (hasNext()) { m_current = m_next; m_prototypes += m_next; m_next = 0; return m_current; } return 0; } const ObjectValue *PrototypeIterator::peekNext() { if (hasNext()) return m_next; return 0; } PrototypeIterator::Error PrototypeIterator::error() const { return m_error; } QList PrototypeIterator::all() { while (hasNext()) next(); return m_prototypes; } FunctionValue::FunctionValue(ValueOwner *valueOwner) : ObjectValue(valueOwner) { setClassName(QLatin1String("Function")); setMember(QLatin1String("length"), valueOwner->numberValue()); setPrototype(valueOwner->functionPrototype()); } FunctionValue::~FunctionValue() { } const Value *FunctionValue::returnValue() const { return valueOwner()->unknownValue(); } int FunctionValue::namedArgumentCount() const { return 0; } const Value *FunctionValue::argument(int) const { return valueOwner()->unknownValue(); } QString FunctionValue::argumentName(int index) const { return QString::fromLatin1("arg%1").arg(index + 1); } int FunctionValue::optionalNamedArgumentCount() const { return 0; } bool FunctionValue::isVariadic() const { return true; } const FunctionValue *FunctionValue::asFunctionValue() const { return this; } void FunctionValue::accept(ValueVisitor *visitor) const { visitor->visit(this); } Function::Function(ValueOwner *valueOwner) : FunctionValue(valueOwner) , _returnValue(0) , _optionalNamedArgumentCount(0) , _isVariadic(false) { } Function::~Function() { } void Function::addArgument(const Value *argument, const QString &name) { if (!name.isEmpty()) { while (_argumentNames.size() < _arguments.size()) _argumentNames.push_back(QString()); _argumentNames.push_back(name); } _arguments.push_back(argument); } const Value *Function::returnValue() const { return _returnValue; } void Function::setReturnValue(const Value *returnValue) { _returnValue = returnValue; } void Function::setVariadic(bool variadic) { _isVariadic = variadic; } void Function::setOptionalNamedArgumentCount(int count) { _optionalNamedArgumentCount = count; } int Function::namedArgumentCount() const { return _arguments.size(); } int Function::optionalNamedArgumentCount() const { return _optionalNamedArgumentCount; } const Value *Function::argument(int index) const { return _arguments.at(index); } QString Function::argumentName(int index) const { if (index < _argumentNames.size()) { const QString name = _argumentNames.at(index); if (!name.isEmpty()) return _argumentNames.at(index); } return FunctionValue::argumentName(index); } bool Function::isVariadic() const { return _isVariadic; } //////////////////////////////////////////////////////////////////////////////// // typing environment //////////////////////////////////////////////////////////////////////////////// CppQmlTypesLoader::BuiltinObjects CppQmlTypesLoader::defaultLibraryObjects; CppQmlTypesLoader::BuiltinObjects CppQmlTypesLoader::defaultQtObjects; CppQmlTypesLoader::BuiltinObjects CppQmlTypesLoader::loadQmlTypes(const QFileInfoList &qmlTypeFiles, QStringList *errors, QStringList *warnings) { QHash newObjects; foreach (const QFileInfo &qmlTypeFile, qmlTypeFiles) { QString error, warning; QFile file(qmlTypeFile.absoluteFilePath()); if (file.open(QIODevice::ReadOnly)) { QByteArray contents = file.readAll(); file.close(); parseQmlTypeDescriptions(contents, &newObjects, 0, &error, &warning, qmlTypeFile.absoluteFilePath()); } else { error = file.errorString(); } if (!error.isEmpty()) { errors->append(TypeDescriptionReader::tr( "Errors while loading qmltypes from %1:\n%2").arg( qmlTypeFile.absoluteFilePath(), error)); } if (!warning.isEmpty()) { warnings->append(TypeDescriptionReader::tr( "Warnings while loading qmltypes from %1:\n%2").arg( qmlTypeFile.absoluteFilePath(), warning)); } } return newObjects; } void CppQmlTypesLoader::parseQmlTypeDescriptions(const QByteArray &contents, BuiltinObjects *newObjects, QList *newModuleApis, QString *errorMessage, QString *warningMessage, const QString &fileName) { if (!contents.isEmpty()) { unsigned char c = contents.at(0); switch (c) { case 0xfe: case 0xef: case 0xff: case 0xee: case 0x00: qWarning() << QApplication::translate("CppQmlTypesLoader", "%1 seems not to be encoded in UTF8 or has a BOM.").arg(fileName); default: break; } } errorMessage->clear(); warningMessage->clear(); TypeDescriptionReader reader(QString::fromUtf8(contents)); if (!reader(newObjects, newModuleApis)) { if (reader.errorMessage().isEmpty()) *errorMessage = QLatin1String("unknown error"); else *errorMessage = reader.errorMessage(); } *warningMessage = reader.warningMessage(); } CppQmlTypes::CppQmlTypes(ValueOwner *valueOwner) : _cppContextProperties(0) , _valueOwner(valueOwner) { } const QLatin1String CppQmlTypes::defaultPackage(""); const QLatin1String CppQmlTypes::cppPackage(""); template void CppQmlTypes::load(const T &fakeMetaObjects, const QString &overridePackage) { QList newCppTypes; foreach (const FakeMetaObject::ConstPtr &fmo, fakeMetaObjects) { foreach (const FakeMetaObject::Export &exp, fmo->exports()) { QString package = exp.package; if (package.isEmpty()) package = overridePackage; _fakeMetaObjectsByPackage[package].insert(fmo); // make versionless cpp types directly // needed for access to property types that are not exported, like QDeclarativeAnchors if (exp.package == cppPackage) { QTC_ASSERT(exp.version == ComponentVersion(), continue); QTC_ASSERT(exp.type == fmo->className(), continue); CppComponentValue *cppValue = new CppComponentValue( fmo, fmo->className(), cppPackage, ComponentVersion(), ComponentVersion(), ComponentVersion::MaxVersion, _valueOwner); _objectsByQualifiedName[qualifiedName(cppPackage, fmo->className(), ComponentVersion())] = cppValue; newCppTypes += cppValue; } } } // set prototypes of cpp types foreach (CppComponentValue *object, newCppTypes) { const QString &protoCppName = object->metaObject()->superclassName(); const CppComponentValue *proto = objectByCppName(protoCppName); if (proto) object->setPrototype(proto); } } // explicitly instantiate load for list and hash template void CppQmlTypes::load< QList >(const QList &, const QString &); template void CppQmlTypes::load< QHash >(const QHash &, const QString &); QList CppQmlTypes::createObjectsForImport(const QString &package, ComponentVersion version) { QHash exportedObjects; QList newObjects; // make new exported objects foreach (const FakeMetaObject::ConstPtr &fmo, _fakeMetaObjectsByPackage.value(package)) { // find the highest-version export for each alias QHash bestExports; foreach (const FakeMetaObject::Export &exp, fmo->exports()) { if (exp.package != package || (version.isValid() && exp.version > version)) continue; if (bestExports.contains(exp.type)) { if (exp.version > bestExports.value(exp.type).version) bestExports.insert(exp.type, exp); } else { bestExports.insert(exp.type, exp); } } if (bestExports.isEmpty()) continue; // if it already exists, skip const QString key = qualifiedName(package, fmo->className(), version); if (_objectsByQualifiedName.contains(key)) continue; foreach (const FakeMetaObject::Export &bestExport, bestExports) { QString name = bestExport.type; bool exported = true; if (name.isEmpty()) { exported = false; name = fmo->className(); } CppComponentValue *newComponent = new CppComponentValue( fmo, name, package, bestExport.version, version, bestExport.metaObjectRevision, _valueOwner); // use package.cppname importversion as key _objectsByQualifiedName.insert(key, newComponent); if (exported) { if (!exportedObjects.contains(name) // we might have the same type in different versions || (newComponent->componentVersion() > exportedObjects.value(name)->componentVersion())) exportedObjects.insert(name, newComponent); } newObjects += newComponent; } } // set their prototypes, creating them if necessary foreach (const CppComponentValue *cobject, newObjects) { CppComponentValue *object = const_cast(cobject); while (!object->prototype()) { const QString &protoCppName = object->metaObject()->superclassName(); if (protoCppName.isEmpty()) break; // if the prototype already exists, done const QString key = qualifiedName(object->moduleName(), protoCppName, version); if (const CppComponentValue *proto = _objectsByQualifiedName.value(key)) { object->setPrototype(proto); break; } // get the fmo via the cpp name const CppComponentValue *cppProto = objectByCppName(protoCppName); if (!cppProto) break; FakeMetaObject::ConstPtr protoFmo = cppProto->metaObject(); // make a new object CppComponentValue *proto = new CppComponentValue( protoFmo, protoCppName, object->moduleName(), ComponentVersion(), object->importVersion(), ComponentVersion::MaxVersion, _valueOwner); _objectsByQualifiedName.insert(key, proto); object->setPrototype(proto); // maybe set prototype of prototype object = proto; } } return exportedObjects.values(); } bool CppQmlTypes::hasModule(const QString &module) const { return _fakeMetaObjectsByPackage.contains(module); } QString CppQmlTypes::qualifiedName(const QString &module, const QString &type, ComponentVersion version) { return QString::fromLatin1("%1/%2 %3").arg( module, type, version.toString()); } const CppComponentValue *CppQmlTypes::objectByQualifiedName(const QString &name) const { return _objectsByQualifiedName.value(name); } const CppComponentValue *CppQmlTypes::objectByQualifiedName(const QString &package, const QString &type, ComponentVersion version) const { return objectByQualifiedName(qualifiedName(package, type, version)); } const CppComponentValue *CppQmlTypes::objectByCppName(const QString &cppName) const { return objectByQualifiedName(qualifiedName(cppPackage, cppName, ComponentVersion())); } void CppQmlTypes::setCppContextProperties(const ObjectValue *contextProperties) { _cppContextProperties = contextProperties; } const ObjectValue *CppQmlTypes::cppContextProperties() const { return _cppContextProperties; } ConvertToNumber::ConvertToNumber(ValueOwner *valueOwner) : _valueOwner(valueOwner), _result(0) { } const Value *ConvertToNumber::operator()(const Value *value) { const Value *previousValue = switchResult(0); if (value) value->accept(this); return switchResult(previousValue); } const Value *ConvertToNumber::switchResult(const Value *value) { const Value *previousResult = _result; _result = value; return previousResult; } void ConvertToNumber::visit(const NullValue *) { _result = _valueOwner->numberValue(); } void ConvertToNumber::visit(const UndefinedValue *) { _result = _valueOwner->numberValue(); } void ConvertToNumber::visit(const NumberValue *value) { _result = value; } void ConvertToNumber::visit(const BooleanValue *) { _result = _valueOwner->numberValue(); } void ConvertToNumber::visit(const StringValue *) { _result = _valueOwner->numberValue(); } void ConvertToNumber::visit(const ObjectValue *object) { if (const FunctionValue *valueOfMember = value_cast( object->lookupMember(QLatin1String("valueOf"), ContextPtr()))) { _result = value_cast(valueOfMember->returnValue()); } } void ConvertToNumber::visit(const FunctionValue *object) { if (const FunctionValue *valueOfMember = value_cast( object->lookupMember(QLatin1String("valueOf"), ContextPtr()))) { _result = value_cast(valueOfMember->returnValue()); } } ConvertToString::ConvertToString(ValueOwner *valueOwner) : _valueOwner(valueOwner), _result(0) { } const Value *ConvertToString::operator()(const Value *value) { const Value *previousValue = switchResult(0); if (value) value->accept(this); return switchResult(previousValue); } const Value *ConvertToString::switchResult(const Value *value) { const Value *previousResult = _result; _result = value; return previousResult; } void ConvertToString::visit(const NullValue *) { _result = _valueOwner->stringValue(); } void ConvertToString::visit(const UndefinedValue *) { _result = _valueOwner->stringValue(); } void ConvertToString::visit(const NumberValue *) { _result = _valueOwner->stringValue(); } void ConvertToString::visit(const BooleanValue *) { _result = _valueOwner->stringValue(); } void ConvertToString::visit(const StringValue *value) { _result = value; } void ConvertToString::visit(const ObjectValue *object) { if (const FunctionValue *toStringMember = value_cast( object->lookupMember(QLatin1String("toString"), ContextPtr()))) { _result = value_cast(toStringMember->returnValue()); } } void ConvertToString::visit(const FunctionValue *object) { if (const FunctionValue *toStringMember = value_cast( object->lookupMember(QLatin1String("toString"), ContextPtr()))) { _result = value_cast(toStringMember->returnValue()); } } ConvertToObject::ConvertToObject(ValueOwner *valueOwner) : _valueOwner(valueOwner), _result(0) { } const Value *ConvertToObject::operator()(const Value *value) { const Value *previousValue = switchResult(0); if (value) value->accept(this); return switchResult(previousValue); } const Value *ConvertToObject::switchResult(const Value *value) { const Value *previousResult = _result; _result = value; return previousResult; } void ConvertToObject::visit(const NullValue *value) { _result = value; } void ConvertToObject::visit(const UndefinedValue *) { _result = _valueOwner->nullValue(); } void ConvertToObject::visit(const NumberValue *) { _result = _valueOwner->numberCtor()->returnValue(); } void ConvertToObject::visit(const BooleanValue *) { _result = _valueOwner->booleanCtor()->returnValue(); } void ConvertToObject::visit(const StringValue *) { _result = _valueOwner->stringCtor()->returnValue(); } void ConvertToObject::visit(const ObjectValue *object) { _result = object; } void ConvertToObject::visit(const FunctionValue *object) { _result = object; } QString TypeId::operator()(const Value *value) { _result = QLatin1String("unknown"); if (value) value->accept(this); return _result; } void TypeId::visit(const NullValue *) { _result = QLatin1String("null"); } void TypeId::visit(const UndefinedValue *) { _result = QLatin1String("undefined"); } void TypeId::visit(const NumberValue *) { _result = QLatin1String("number"); } void TypeId::visit(const BooleanValue *) { _result = QLatin1String("boolean"); } void TypeId::visit(const StringValue *) { _result = QLatin1String("string"); } void TypeId::visit(const ObjectValue *object) { _result = object->className(); if (_result.isEmpty()) _result = QLatin1String("object"); } void TypeId::visit(const FunctionValue *object) { _result = object->className(); if (_result.isEmpty()) _result = QLatin1String("Function"); } void TypeId::visit(const ColorValue *) { _result = QLatin1String("string"); } void TypeId::visit(const AnchorLineValue *) { _result = QLatin1String("AnchorLine"); } ASTObjectValue::ASTObjectValue(UiQualifiedId *typeName, UiObjectInitializer *initializer, const Document *doc, ValueOwner *valueOwner) : ObjectValue(valueOwner), _typeName(typeName), _initializer(initializer), _doc(doc), _defaultPropertyRef(0) { if (_initializer) { for (UiObjectMemberList *it = _initializer->members; it; it = it->next) { UiObjectMember *member = it->member; if (UiPublicMember *def = cast(member)) { if (def->type == UiPublicMember::Property && !def->name.isEmpty() && !def->memberType.isEmpty()) { ASTPropertyReference *ref = new ASTPropertyReference(def, _doc, valueOwner); _properties.append(ref); if (def->defaultToken.isValid()) _defaultPropertyRef = ref; } else if (def->type == UiPublicMember::Signal && !def->name.isEmpty()) { ASTSignal *ref = new ASTSignal(def, _doc, valueOwner); _signals.append(ref); } } } } } ASTObjectValue::~ASTObjectValue() { } const ASTObjectValue *ASTObjectValue::asAstObjectValue() const { return this; } bool ASTObjectValue::getSourceLocation(QString *fileName, int *line, int *column) const { *fileName = _doc->fileName(); *line = _typeName->identifierToken.startLine; *column = _typeName->identifierToken.startColumn; return true; } void ASTObjectValue::processMembers(MemberProcessor *processor) const { foreach (ASTPropertyReference *ref, _properties) { processor->processProperty(ref->ast()->name.toString(), ref); // ### Should get a different value? processor->processGeneratedSlot(ref->onChangedSlotName(), ref); } foreach (ASTSignal *ref, _signals) { processor->processSignal(ref->ast()->name.toString(), ref); // ### Should get a different value? processor->processGeneratedSlot(ref->slotName(), ref); } ObjectValue::processMembers(processor); } QString ASTObjectValue::defaultPropertyName() const { if (_defaultPropertyRef) { UiPublicMember *prop = _defaultPropertyRef->ast(); if (prop) return prop->name.toString(); } return QString(); } UiObjectInitializer *ASTObjectValue::initializer() const { return _initializer; } UiQualifiedId *ASTObjectValue::typeName() const { return _typeName; } const Document *ASTObjectValue::document() const { return _doc; } ASTVariableReference::ASTVariableReference(VariableDeclaration *ast, const Document *doc, ValueOwner *valueOwner) : Reference(valueOwner) , _ast(ast) , _doc(doc) { } ASTVariableReference::~ASTVariableReference() { } const Value *ASTVariableReference::value(ReferenceContext *referenceContext) const { // may be assigned to later if (!_ast->expression) return valueOwner()->unknownValue(); Document::Ptr doc = _doc->ptr(); ScopeChain scopeChain(doc, referenceContext->context()); ScopeBuilder builder(&scopeChain); builder.push(ScopeAstPath(doc)(_ast->expression->firstSourceLocation().begin())); Evaluate evaluator(&scopeChain, referenceContext); return evaluator(_ast->expression); } bool ASTVariableReference::getSourceLocation(QString *fileName, int *line, int *column) const { *fileName = _doc->fileName(); *line = _ast->identifierToken.startLine; *column = _ast->identifierToken.startColumn; return true; } namespace { class UsesArgumentsArray : protected Visitor { bool _usesArgumentsArray; public: bool operator()(FunctionBody *ast) { if (!ast || !ast->elements) return false; _usesArgumentsArray = false; Node::accept(ast->elements, this); return _usesArgumentsArray; } protected: bool visit(ArrayMemberExpression *ast) { if (IdentifierExpression *idExp = cast(ast->base)) { if (idExp->name == QLatin1String("arguments")) _usesArgumentsArray = true; } return true; } // don't go into nested functions bool visit(FunctionBody *) { return false; } }; } // anonymous namespace ASTFunctionValue::ASTFunctionValue(FunctionExpression *ast, const Document *doc, ValueOwner *valueOwner) : FunctionValue(valueOwner) , _ast(ast) , _doc(doc) { setPrototype(valueOwner->functionPrototype()); for (FormalParameterList *it = ast->formals; it; it = it->next) _argumentNames.append(it->name.toString()); _isVariadic = UsesArgumentsArray()(ast->body); } ASTFunctionValue::~ASTFunctionValue() { } FunctionExpression *ASTFunctionValue::ast() const { return _ast; } int ASTFunctionValue::namedArgumentCount() const { return _argumentNames.size(); } QString ASTFunctionValue::argumentName(int index) const { if (index < _argumentNames.size()) { const QString &name = _argumentNames.at(index); if (!name.isEmpty()) return name; } return FunctionValue::argumentName(index); } bool ASTFunctionValue::isVariadic() const { return _isVariadic; } bool ASTFunctionValue::getSourceLocation(QString *fileName, int *line, int *column) const { *fileName = _doc->fileName(); *line = _ast->identifierToken.startLine; *column = _ast->identifierToken.startColumn; return true; } QmlPrototypeReference::QmlPrototypeReference(UiQualifiedId *qmlTypeName, const Document *doc, ValueOwner *valueOwner) : Reference(valueOwner), _qmlTypeName(qmlTypeName), _doc(doc) { } QmlPrototypeReference::~QmlPrototypeReference() { } const QmlPrototypeReference *QmlPrototypeReference::asQmlPrototypeReference() const { return this; } UiQualifiedId *QmlPrototypeReference::qmlTypeName() const { return _qmlTypeName; } const Value *QmlPrototypeReference::value(ReferenceContext *referenceContext) const { return referenceContext->context()->lookupType(_doc, _qmlTypeName); } ASTPropertyReference::ASTPropertyReference(UiPublicMember *ast, const Document *doc, ValueOwner *valueOwner) : Reference(valueOwner), _ast(ast), _doc(doc) { const QString &propertyName = ast->name.toString(); _onChangedSlotName = generatedSlotName(propertyName); _onChangedSlotName += QLatin1String("Changed"); } ASTPropertyReference::~ASTPropertyReference() { } const ASTPropertyReference *ASTPropertyReference::asAstPropertyReference() const { return this; } bool ASTPropertyReference::getSourceLocation(QString *fileName, int *line, int *column) const { *fileName = _doc->fileName(); *line = _ast->identifierToken.startLine; *column = _ast->identifierToken.startColumn; return true; } const Value *ASTPropertyReference::value(ReferenceContext *referenceContext) const { if (_ast->statement && (_ast->memberType.isEmpty() || _ast->memberType == QLatin1String("variant") || _ast->memberType == QLatin1String("var") || _ast->memberType == QLatin1String("alias"))) { // Adjust the context for the current location - expensive! // ### Improve efficiency by caching the 'use chain' constructed in ScopeBuilder. Document::Ptr doc = _doc->ptr(); ScopeChain scopeChain(doc, referenceContext->context()); ScopeBuilder builder(&scopeChain); int offset = _ast->statement->firstSourceLocation().begin(); builder.push(ScopeAstPath(doc)(offset)); Evaluate evaluator(&scopeChain, referenceContext); return evaluator(_ast->statement); } const QString memberType = _ast->memberType.toString(); const Value *builtin = valueOwner()->defaultValueForBuiltinType(memberType); if (!builtin->asUndefinedValue()) return builtin; if (_ast->typeModifier.isEmpty()) { const Value *type = referenceContext->context()->lookupType(_doc, QStringList(memberType)); if (type) return type; } return referenceContext->context()->valueOwner()->undefinedValue(); } ASTSignal::ASTSignal(UiPublicMember *ast, const Document *doc, ValueOwner *valueOwner) : FunctionValue(valueOwner), _ast(ast), _doc(doc) { const QString &signalName = ast->name.toString(); _slotName = generatedSlotName(signalName); ObjectValue *v = valueOwner->newObject(/*prototype=*/0); for (UiParameterList *it = ast->parameters; it; it = it->next) { if (!it->name.isEmpty()) v->setMember(it->name.toString(), valueOwner->defaultValueForBuiltinType(it->type.toString())); } _bodyScope = v; } ASTSignal::~ASTSignal() { } const ASTSignal *ASTSignal::asAstSignal() const { return this; } int ASTSignal::namedArgumentCount() const { int count = 0; for (UiParameterList *it = _ast->parameters; it; it = it->next) ++count; return count; } const Value *ASTSignal::argument(int index) const { UiParameterList *param = _ast->parameters; for (int i = 0; param && i < index; ++i) param = param->next; if (!param || param->type.isEmpty()) return valueOwner()->unknownValue(); return valueOwner()->defaultValueForBuiltinType(param->type.toString()); } QString ASTSignal::argumentName(int index) const { UiParameterList *param = _ast->parameters; for (int i = 0; param && i < index; ++i) param = param->next; if (!param || param->name.isEmpty()) return FunctionValue::argumentName(index); return param->name.toString(); } bool ASTSignal::getSourceLocation(QString *fileName, int *line, int *column) const { *fileName = _doc->fileName(); *line = _ast->identifierToken.startLine; *column = _ast->identifierToken.startColumn; return true; } ImportInfo::ImportInfo() : _type(InvalidImport) , _ast(0) { } ImportInfo ImportInfo::moduleImport(QString uri, ComponentVersion version, const QString &as, UiImport *ast) { // treat Qt 4.7 as QtQuick 1.0 if (uri == QLatin1String("Qt") && version == ComponentVersion(4, 7)) { uri = QLatin1String("QtQuick"); version = ComponentVersion(1, 0); } ImportInfo info; info._type = LibraryImport; info._name = uri; info._path = uri; info._path.replace(QLatin1Char('.'), QDir::separator()); info._version = version; info._as = as; info._ast = ast; return info; } ImportInfo ImportInfo::pathImport(const QString &docPath, const QString &path, ComponentVersion version, const QString &as, UiImport *ast) { ImportInfo info; info._name = path; QFileInfo importFileInfo(path); if (!importFileInfo.isAbsolute()) importFileInfo = QFileInfo(docPath + QDir::separator() + path); info._path = importFileInfo.absoluteFilePath(); if (importFileInfo.isFile()) { info._type = FileImport; } else if (importFileInfo.isDir()) { info._type = DirectoryImport; } else if (path.startsWith(QLatin1String("qrc:"))) { info._path = path; if (ModelManagerInterface::instance()->filesAtQrcPath(info.path()).isEmpty()) info._type = QrcDirectoryImport; else info._type = QrcFileImport; } else { info._type = UnknownFileImport; } info._version = version; info._as = as; info._ast = ast; return info; } ImportInfo ImportInfo::invalidImport(UiImport *ast) { ImportInfo info; info._type = InvalidImport; info._ast = ast; return info; } ImportInfo ImportInfo::implicitDirectoryImport(const QString &directory) { ImportInfo info; info._type = ImplicitDirectoryImport; info._path = directory; return info; } bool ImportInfo::isValid() const { return _type != InvalidImport; } ImportInfo::Type ImportInfo::type() const { return _type; } QString ImportInfo::name() const { return _name; } QString ImportInfo::path() const { return _path; } QString ImportInfo::as() const { return _as; } ComponentVersion ImportInfo::version() const { return _version; } UiImport *ImportInfo::ast() const { return _ast; } Import::Import() : object(0) {} TypeScope::TypeScope(const Imports *imports, ValueOwner *valueOwner) : ObjectValue(valueOwner) , _imports(imports) { } const Value *TypeScope::lookupMember(const QString &name, const Context *context, const ObjectValue **foundInObject, bool) const { QListIterator it(_imports->all()); it.toBack(); while (it.hasPrevious()) { const Import &i = it.previous(); const ObjectValue *import = i.object; const ImportInfo &info = i.info; // JS import has no types if (info.type() == ImportInfo::FileImport || info.type() == ImportInfo::QrcFileImport) continue; if (!info.as().isEmpty()) { if (info.as() == name) { if (foundInObject) *foundInObject = this; return import; } continue; } if (const Value *v = import->lookupMember(name, context, foundInObject)) return v; } if (foundInObject) *foundInObject = 0; return 0; } void TypeScope::processMembers(MemberProcessor *processor) const { QListIterator it(_imports->all()); it.toBack(); while (it.hasPrevious()) { const Import &i = it.previous(); const ObjectValue *import = i.object; const ImportInfo &info = i.info; // JS import has no types if (info.type() == ImportInfo::FileImport || info.type() == ImportInfo::QrcFileImport) continue; if (!info.as().isEmpty()) processor->processProperty(info.as(), import); else import->processMembers(processor); } } JSImportScope::JSImportScope(const Imports *imports, ValueOwner *valueOwner) : ObjectValue(valueOwner) , _imports(imports) { } const Value *JSImportScope::lookupMember(const QString &name, const Context *, const ObjectValue **foundInObject, bool) const { QListIterator it(_imports->all()); it.toBack(); while (it.hasPrevious()) { const Import &i = it.previous(); const ObjectValue *import = i.object; const ImportInfo &info = i.info; // JS imports are always: import "somefile.js" as Foo if (info.type() != ImportInfo::FileImport && info.type() != ImportInfo::QrcFileImport) continue; if (info.as() == name) { if (foundInObject) *foundInObject = this; return import; } } if (foundInObject) *foundInObject = 0; return 0; } void JSImportScope::processMembers(MemberProcessor *processor) const { QListIterator it(_imports->all()); it.toBack(); while (it.hasPrevious()) { const Import &i = it.previous(); const ObjectValue *import = i.object; const ImportInfo &info = i.info; if (info.type() == ImportInfo::FileImport || info.type() == ImportInfo::QrcFileImport) processor->processProperty(info.as(), import); } } Imports::Imports(ValueOwner *valueOwner) : _typeScope(new TypeScope(this, valueOwner)) , _jsImportScope(new JSImportScope(this, valueOwner)) , _importFailed(false) {} void Imports::append(const Import &import) { // when doing lookup, imports with 'as' clause are looked at first if (!import.info.as().isEmpty()) { _imports.append(import); } else { // find first as-import and prepend for (int i = 0; i < _imports.size(); ++i) { if (!_imports.at(i).info.as().isEmpty()) { _imports.insert(i, import); return; } } // not found, append _imports.append(import); } if (!import.valid) _importFailed = true; } void Imports::setImportFailed() { _importFailed = true; } ImportInfo Imports::info(const QString &name, const Context *context) const { QString firstId = name; int dotIdx = firstId.indexOf(QLatin1Char('.')); if (dotIdx != -1) firstId = firstId.left(dotIdx); QListIterator it(_imports); it.toBack(); while (it.hasPrevious()) { const Import &i = it.previous(); const ObjectValue *import = i.object; const ImportInfo &info = i.info; if (!info.as().isEmpty()) { if (info.as() == firstId) return info; continue; } if (info.type() == ImportInfo::FileImport || info.type() == ImportInfo::QrcFileImport) { if (import->className() == firstId) return info; } else { if (import->lookupMember(firstId, context)) return info; } } return ImportInfo(); } QString Imports::nameForImportedObject(const ObjectValue *value, const Context *context) const { QListIterator it(_imports); it.toBack(); while (it.hasPrevious()) { const Import &i = it.previous(); const ObjectValue *import = i.object; const ImportInfo &info = i.info; if (info.type() == ImportInfo::FileImport || info.type() == ImportInfo::QrcFileImport) { if (import == value) return import->className(); } else { const Value *v = import->lookupMember(value->className(), context); if (v == value) { QString result = value->className(); if (!info.as().isEmpty()) { result.prepend(QLatin1Char('.')); result.prepend(info.as()); } return result; } } } return QString(); } bool Imports::importFailed() const { return _importFailed; } QList Imports::all() const { return _imports; } const TypeScope *Imports::typeScope() const { return _typeScope; } const JSImportScope *Imports::jsImportScope() const { return _jsImportScope; } #ifdef QT_DEBUG class MemberDumper: public MemberProcessor { public: MemberDumper() {} virtual bool processProperty(const QString &name, const Value *) { qDebug() << "property: " << name; return true; } virtual bool processEnumerator(const QString &name, const Value *) { qDebug() << "enumerator: " << name; return true; } virtual bool processSignal(const QString &name, const Value *) { qDebug() << "signal: " << name; return true; } virtual bool processSlot(const QString &name, const Value *) { qDebug() << "slot: " << name; return true; } virtual bool processGeneratedSlot(const QString &name, const Value *) { qDebug() << "generated slot: " << name; return true; } }; void Imports::dump() const { qDebug() << "Imports contents, in search order:"; QListIterator it(_imports); it.toBack(); while (it.hasPrevious()) { const Import &i = it.previous(); const ObjectValue *import = i.object; const ImportInfo &info = i.info; qDebug() << " " << info.path() << " " << info.version().toString() << " as " << info.as() << " : " << import; MemberDumper dumper; import->processMembers(&dumper); } } #endif