diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp index c699508463b8970ca659edf29bf445b4a4e445fb..a0b42b0c0205069be15c2a6aacbfe6e1cfd0766e 100644 --- a/src/libs/qmljs/qmljsbind.cpp +++ b/src/libs/qmljs/qmljsbind.cpp @@ -92,6 +92,19 @@ Interpreter::ObjectValue *Bind::findQmlObject(AST::Node *node) const return _qmlObjects.value(node); } +bool Bind::usesQmlPrototype(ObjectValue *prototype, + Context *context) const +{ + foreach (ObjectValue *object, _qmlObjects.values()) { + const ObjectValue *resolvedPrototype = object->prototype(context); + + if (resolvedPrototype == prototype) + return true; + } + + return false; +} + ObjectValue *Bind::switchObjectValue(ObjectValue *newObjectValue) { ObjectValue *oldObjectValue = _currentObjectValue; @@ -195,8 +208,8 @@ bool Bind::visit(AST::UiProgram *) bool Bind::visit(AST::Program *) { - _currentObjectValue = _engine.globalObject(); - _rootObjectValue = _engine.globalObject(); + _currentObjectValue = _engine.newObject(/*prototype =*/ 0); + _rootObjectValue = _currentObjectValue; return true; } diff --git a/src/libs/qmljs/qmljsbind.h b/src/libs/qmljs/qmljsbind.h index eacc943cf13d5ae41627b328c556a375f5512101..eaaababa97ed7495db293842d62a5d1d0c95b5ad 100644 --- a/src/libs/qmljs/qmljsbind.h +++ b/src/libs/qmljs/qmljsbind.h @@ -59,6 +59,8 @@ public: Interpreter::ObjectValue *rootObjectValue() const; Interpreter::ObjectValue *findQmlObject(AST::Node *node) const; + bool usesQmlPrototype(Interpreter::ObjectValue *prototype, + Interpreter::Context *context) const; static QString toString(AST::UiQualifiedId *qualifiedId, QChar delimiter = QChar('.')); diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index 5e9c645ae943b9812a5a5a223a05d5e012987548..5b9d38fa291a35de424bb02ff0288a498cf81d13 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -31,28 +31,41 @@ Interpreter::Engine *Link::engine() void Link::scopeChainAt(Document::Ptr doc, Node *currentObject) { + // ### TODO: This object ought to contain the global namespace additions by QML. _context->pushScope(engine()->globalObject()); if (! doc) return; - if (doc->qmlProgram() != 0) - _context->setLookupMode(Context::QmlLookup); - Bind *bind = doc->bind(); + QStringList linkedDocs; // to prevent cycles - // Build the scope chain. + if (doc->qmlProgram()) { + _context->setLookupMode(Context::QmlLookup); + + ObjectValue *scopeObject = 0; + if (UiObjectDefinition *definition = cast<UiObjectDefinition *>(currentObject)) + scopeObject = bind->findQmlObject(definition); + else if (UiObjectBinding *binding = cast<UiObjectBinding *>(currentObject)) + scopeObject = bind->findQmlObject(binding); + + pushScopeChainForComponent(doc, &linkedDocs, scopeObject); + + if (const ObjectValue *typeEnvironment = _context->typeEnvironment(doc.data())) + _context->pushScope(typeEnvironment); + } else { + // add scope chains for all components that source this document + foreach (Document::Ptr otherDoc, _docs) { + if (otherDoc->bind()->includedScripts().contains(doc->fileName())) + pushScopeChainForComponent(otherDoc, &linkedDocs); + } - // ### FIXME: May want to link to instantiating components from here. + // ### TODO: Which type environment do scripts see? - ObjectValue *scopeObject = 0; - if (UiObjectDefinition *definition = cast<UiObjectDefinition *>(currentObject)) - scopeObject = bind->findQmlObject(definition); - else if (UiObjectBinding *binding = cast<UiObjectBinding *>(currentObject)) - scopeObject = bind->findQmlObject(binding); - else if (FunctionDeclaration *fun = cast<FunctionDeclaration *>(currentObject)) { _context->pushScope(bind->rootObjectValue()); + } + if (FunctionDeclaration *fun = cast<FunctionDeclaration *>(currentObject)) { ObjectValue *activation = engine()->newObject(/*prototype = */ 0); for (FormalParameterList *it = fun->formals; it; it = it->next) { if (it->name) @@ -60,15 +73,35 @@ void Link::scopeChainAt(Document::Ptr doc, Node *currentObject) } _context->pushScope(activation); } +} + +void Link::pushScopeChainForComponent(Document::Ptr doc, QStringList *linkedDocs, + ObjectValue *scopeObject) +{ + if (!doc->qmlProgram()) + return; - if (scopeObject) { - if (bind->rootObjectValue()) - _context->pushScope(bind->rootObjectValue()); + linkedDocs->append(doc->fileName()); - if (scopeObject != bind->rootObjectValue()) - _context->pushScope(scopeObject); + Bind *bind = doc->bind(); + + // add scopes for all components instantiating this one + foreach (Document::Ptr otherDoc, _docs) { + if (linkedDocs->contains(otherDoc->fileName())) + continue; + + if (otherDoc->bind()->usesQmlPrototype(bind->rootObjectValue(), _context)) { + // ### TODO: Depth-first insertion doesn't give us correct name shadowing. + pushScopeChainForComponent(otherDoc, linkedDocs); + } } + if (bind->rootObjectValue()) + _context->pushScope(bind->rootObjectValue()); + + if (scopeObject && scopeObject != bind->rootObjectValue()) + _context->pushScope(scopeObject); + const QStringList &includedScripts = bind->includedScripts(); for (int index = includedScripts.size() - 1; index != -1; --index) { const QString &scriptFile = includedScripts.at(index); @@ -82,9 +115,6 @@ void Link::scopeChainAt(Document::Ptr doc, Node *currentObject) _context->pushScope(bind->functionEnvironment()); _context->pushScope(bind->idEnvironment()); - - if (const ObjectValue *typeEnvironment = _context->typeEnvironment(doc.data())) - _context->pushScope(typeEnvironment); } void Link::linkImports() @@ -251,40 +281,85 @@ UiQualifiedId *Link::qualifiedTypeNameId(Node *node) return 0; } +static uint qHash(Document::Ptr doc) { + return qHash(doc.data()); +} + QList<Document::Ptr> Link::reachableDocuments(Document::Ptr startDoc, const Snapshot &snapshot) { - QList<Document::Ptr> docs; + QSet<Document::Ptr> docs; if (! startDoc) - return docs; - - QSet<QString> processed; - QStringList todo; + return docs.values(); QMultiHash<QString, Document::Ptr> documentByPath; foreach (Document::Ptr doc, snapshot) documentByPath.insert(doc->path(), doc); - todo.append(startDoc->path()); - - // Find the reachable documents. - while (! todo.isEmpty()) { - const QString path = todo.takeFirst(); + // ### TODO: This doesn't scale well. Maybe just use the whole snapshot? + // Find all documents that (indirectly) include startDoc + { + QList<Document::Ptr> todo; + todo += startDoc; - if (processed.contains(path)) - continue; - - processed.insert(path); + while (! todo.isEmpty()) { + Document::Ptr doc = todo.takeFirst(); - QStringList localImports; - foreach (Document::Ptr doc, documentByPath.values(path)) { docs += doc; - localImports += doc->bind()->localImports(); + + Snapshot::const_iterator it, end = snapshot.end(); + for (it = snapshot.begin(); it != end; ++it) { + Document::Ptr otherDoc = *it; + if (docs.contains(otherDoc)) + continue; + + QStringList localImports = otherDoc->bind()->localImports(); + if (localImports.contains(doc->fileName()) + || localImports.contains(doc->path()) + || otherDoc->bind()->includedScripts().contains(doc->fileName()) + ) { + todo += otherDoc; + } + } } + } - localImports.removeDuplicates(); - todo += localImports; + // Find all documents that are included by these (even if indirectly). + { + QSet<QString> processed; + QStringList todo; + foreach (Document::Ptr doc, docs) + todo.append(doc->fileName()); + + while (! todo.isEmpty()) { + QString path = todo.takeFirst(); + + if (processed.contains(path)) + continue; + processed.insert(path); + + if (Document::Ptr doc = snapshot.document(path)) { + docs += doc; + + if (doc->qmlProgram()) + path = doc->path(); + else + continue; + } + + QStringList localImports; + foreach (Document::Ptr doc, documentByPath.values(path)) { + if (doc->qmlProgram()) { + docs += doc; + localImports += doc->bind()->localImports(); + localImports += doc->bind()->includedScripts(); + } + } + + localImports.removeDuplicates(); + todo += localImports; + } } - return docs; + return docs.values(); } diff --git a/src/libs/qmljs/qmljslink.h b/src/libs/qmljs/qmljslink.h index c6329fef324c0f24c78cb8dfe81cd429aacbd2df..936851dac54c0274e84a76baa6eb66604edc7b3e 100644 --- a/src/libs/qmljs/qmljslink.h +++ b/src/libs/qmljs/qmljslink.h @@ -28,6 +28,9 @@ public: private: Interpreter::Engine *engine(); + void pushScopeChainForComponent(Document::Ptr doc, QStringList *linkedDocs, + Interpreter::ObjectValue *scopeObject = 0); + static QList<Document::Ptr> reachableDocuments(Document::Ptr startDoc, const Snapshot &snapshot); static AST::UiQualifiedId *qualifiedTypeNameId(AST::Node *node);