forked from qt-creator/qt-creator
		
	Initial work on the binding pass.
This commit is contained in:
		
							
								
								
									
										569
									
								
								src/libs/cplusplus/CppBindings.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										569
									
								
								src/libs/cplusplus/CppBindings.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,569 @@ | ||||
| /************************************************************************** | ||||
| ** | ||||
| ** This file is part of Qt Creator | ||||
| ** | ||||
| ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). | ||||
| ** | ||||
| ** Contact:  Qt Software Information (qt-info@nokia.com) | ||||
| ** | ||||
| ** Commercial Usage | ||||
| ** | ||||
| ** Licensees holding valid Qt Commercial licenses may use this file in | ||||
| ** accordance with the Qt Commercial License Agreement provided with the | ||||
| ** Software or, alternatively, in accordance with the terms contained in | ||||
| ** a written agreement between you and Nokia. | ||||
| ** | ||||
| ** 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. | ||||
| ** | ||||
| ** If you are unsure which license is appropriate for your use, please | ||||
| ** contact the sales department at qt-sales@nokia.com. | ||||
| ** | ||||
| **************************************************************************/ | ||||
|  | ||||
| #include "CppBindings.h" | ||||
| #include "CppDocument.h" | ||||
| #include "Overview.h" | ||||
|  | ||||
| #include <CoreTypes.h> | ||||
| #include <Symbols.h> | ||||
| #include <Literals.h> | ||||
| #include <Names.h> | ||||
| #include <Scope.h> | ||||
| #include <Control.h> | ||||
| #include <SymbolVisitor.h> | ||||
|  | ||||
| #include <QtDebug> | ||||
|  | ||||
| using namespace CPlusPlus; | ||||
|  | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////// | ||||
| // Location | ||||
| //////////////////////////////////////////////////////////////////////////////// | ||||
| Location::Location() | ||||
|     : _fileId(0), | ||||
|       _sourceLocation(0) | ||||
| { } | ||||
|  | ||||
| Location::Location(Symbol *symbol) | ||||
|     : _fileId(symbol->fileId()), | ||||
|       _sourceLocation(symbol->sourceLocation()) | ||||
| { } | ||||
|  | ||||
| Location::Location(StringLiteral *fileId, unsigned sourceLocation) | ||||
|     : _fileId(fileId), _sourceLocation(sourceLocation) | ||||
| { } | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////// | ||||
| // NamespaceBinding | ||||
| //////////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| NamespaceBinding::NamespaceBinding(NamespaceBinding *parent) | ||||
|     : parent(parent), | ||||
|       anonymousNamespaceBinding(0) | ||||
| { | ||||
|     if (parent) | ||||
|         parent->children.append(this); | ||||
| } | ||||
|  | ||||
| NamespaceBinding::~NamespaceBinding() | ||||
| { | ||||
|     qDeleteAll(children); | ||||
|     qDeleteAll(classBindings); | ||||
| } | ||||
|  | ||||
| NameId *NamespaceBinding::name() const | ||||
| { | ||||
|     if (symbols.size()) { | ||||
|         if (Name *name = symbols.at(0)->name()) { | ||||
|             NameId *nameId = name->asNameId(); | ||||
|             Q_ASSERT(nameId != 0); | ||||
|  | ||||
|             return nameId; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| Identifier *NamespaceBinding::identifier() const | ||||
| { | ||||
|     if (NameId *nameId = name()) | ||||
|         return nameId->identifier(); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| NamespaceBinding *NamespaceBinding::globalNamespaceBinding() | ||||
| { | ||||
|     NamespaceBinding *it = this; | ||||
|  | ||||
|     for (; it; it = it->parent) { | ||||
|         if (! it->parent) | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|     return it; | ||||
| } | ||||
|  | ||||
| NamespaceBinding *NamespaceBinding::findNamespaceBinding(Name *name) | ||||
| { | ||||
|     if (! name) | ||||
|         return anonymousNamespaceBinding; | ||||
|  | ||||
|     else if (NameId *nameId = name->asNameId()) | ||||
|         return findNamespaceBindingForNameId(nameId); | ||||
|  | ||||
|     // invalid binding | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| NamespaceBinding *NamespaceBinding::findNamespaceBindingForNameId(NameId *name) | ||||
| { | ||||
|     foreach (NamespaceBinding *binding, children) { | ||||
|         Name *bindingName = binding->name(); | ||||
|  | ||||
|         if (! bindingName) | ||||
|             continue; | ||||
|  | ||||
|         if (NameId *bindingNameId = bindingName->asNameId()) { | ||||
|             if (name->isEqualTo(bindingNameId)) | ||||
|                 return binding; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| NamespaceBinding *NamespaceBinding::findOrCreateNamespaceBinding(Namespace *symbol) | ||||
| { | ||||
|     if (NamespaceBinding *binding = findNamespaceBinding(symbol->name())) { | ||||
|         int index = 0; | ||||
|  | ||||
|         for (; index < binding->symbols.size(); ++index) { | ||||
|             Namespace *ns = binding->symbols.at(index); | ||||
|  | ||||
|             if (ns == symbol) | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         if (index == binding->symbols.size()) | ||||
|             binding->symbols.append(symbol); | ||||
|  | ||||
|         return binding; | ||||
|     } | ||||
|  | ||||
|     NamespaceBinding *binding = new NamespaceBinding(this); | ||||
|     binding->symbols.append(symbol); | ||||
|  | ||||
|     if (! symbol->name()) { | ||||
|         Q_ASSERT(! anonymousNamespaceBinding); | ||||
|  | ||||
|         anonymousNamespaceBinding = binding; | ||||
|     } | ||||
|  | ||||
|     return binding; | ||||
| } | ||||
|  | ||||
| static void closure(const Location &loc, | ||||
|                     NamespaceBinding *binding, Name *name, | ||||
|                     QList<NamespaceBinding *> *bindings) | ||||
| { | ||||
|     if (bindings->contains(binding)) | ||||
|         return; | ||||
|  | ||||
|     bindings->append(binding); | ||||
|  | ||||
|     Q_ASSERT(name->isNameId()); | ||||
|  | ||||
|     Identifier *id = name->asNameId()->identifier(); | ||||
|     bool ignoreUsingDirectives = false; | ||||
|  | ||||
|     foreach (Namespace *symbol, binding->symbols) { | ||||
|         Scope *scope = symbol->members(); | ||||
|  | ||||
|         for (Symbol *symbol = scope->lookat(id); symbol; symbol = symbol->next()) { | ||||
|             if (symbol->name() != name || ! symbol->isNamespace()) | ||||
|                 continue; | ||||
|  | ||||
|             const Location l(symbol); | ||||
|  | ||||
|             if (l.fileId() == loc.fileId() && l.sourceLocation() < loc.sourceLocation()) { | ||||
|                 ignoreUsingDirectives = true; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (ignoreUsingDirectives) | ||||
|         return; | ||||
|  | ||||
|     foreach (NamespaceBinding *u, binding->usings) | ||||
|         closure(loc, u, name, bindings); | ||||
| } | ||||
|  | ||||
|  | ||||
| NamespaceBinding *NamespaceBinding::resolveNamespace(const Location &loc, | ||||
|                                                      Name *name, | ||||
|                                                      bool lookAtParent) | ||||
| { | ||||
|     if (! name) | ||||
|         return 0; | ||||
|  | ||||
|     else if (NameId *nameId = name->asNameId()) { | ||||
|         QList<NamespaceBinding *> bindings; | ||||
|         closure(loc, this, nameId, &bindings); | ||||
|  | ||||
|         QList<NamespaceBinding *> results; | ||||
|  | ||||
|         foreach (NamespaceBinding *binding, bindings) { | ||||
|             if (NamespaceBinding *b = binding->findNamespaceBinding(nameId)) | ||||
|                 results.append(b); | ||||
|         } | ||||
|  | ||||
|         if (results.size() == 1) | ||||
|             return results.at(0); | ||||
|  | ||||
|         else if (results.size() > 1) { | ||||
|             // ### FIXME: return 0; | ||||
|             return results.at(0); | ||||
|         } | ||||
|  | ||||
|         else if (parent && lookAtParent) | ||||
|             return parent->resolveNamespace(loc, name); | ||||
|  | ||||
|     } else if (QualifiedNameId *q = name->asQualifiedNameId()) { | ||||
|         if (q->nameCount() == 1) { | ||||
|             Q_ASSERT(q->isGlobal()); | ||||
|  | ||||
|             return globalNamespaceBinding()->resolveNamespace(loc, q->nameAt(0)); | ||||
|         } | ||||
|  | ||||
|         NamespaceBinding *current = this; | ||||
|         if (q->isGlobal()) | ||||
|             current = globalNamespaceBinding(); | ||||
|  | ||||
|         current = current->resolveNamespace(loc, q->nameAt(0)); | ||||
|         for (unsigned i = 1; current && i < q->nameCount(); ++i) | ||||
|             current = current->resolveNamespace(loc, q->nameAt(i), false); | ||||
|  | ||||
|         return current; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| // ### rewrite me | ||||
| QByteArray NamespaceBinding::qualifiedId() const | ||||
| { | ||||
|     if (! parent) | ||||
|         return "<root>"; | ||||
|  | ||||
|     QByteArray s; | ||||
|  | ||||
|     s.append(parent->qualifiedId()); | ||||
|     s.append("::"); | ||||
|  | ||||
|     if (Identifier *id = identifier()) | ||||
|         s.append(id->chars(), id->size()); | ||||
|  | ||||
|     else | ||||
|         s.append("<anonymous>"); | ||||
|  | ||||
|     return s; | ||||
| } | ||||
|  | ||||
| // ### rewrite me | ||||
| QByteArray ClassBinding::qualifiedId() const | ||||
| { | ||||
|     QByteArray s = parent->qualifiedId(); | ||||
|     s += "::"; | ||||
|  | ||||
|     if (Identifier *id = identifier()) | ||||
|         s.append(id->chars(), id->size()); | ||||
|  | ||||
|     else | ||||
|         s.append("<anonymous>"); | ||||
|  | ||||
|     return s; | ||||
| } | ||||
|  | ||||
| static int depth; | ||||
|  | ||||
| void NamespaceBinding::dump() | ||||
| { | ||||
|     qDebug() << QByteArray(depth, ' ').constData() << "namespace" << qualifiedId().constData() | ||||
|               << " # " << symbols.size(); | ||||
|  | ||||
|     ++depth; | ||||
|  | ||||
|     foreach (ClassBinding *classBinding, classBindings) { | ||||
|         classBinding->dump(); | ||||
|     } | ||||
|  | ||||
|     foreach (NamespaceBinding *child, children) { | ||||
|         child->dump(); | ||||
|     } | ||||
|  | ||||
|     --depth; | ||||
| } | ||||
|  | ||||
| void ClassBinding::dump() | ||||
| { | ||||
|     qDebug() << QByteArray(depth, ' ').constData() << "class" << qualifiedId().constData() | ||||
|               << " # " << symbols.size(); | ||||
|  | ||||
|     ++depth; | ||||
|  | ||||
|     foreach (ClassBinding *classBinding, children) { | ||||
|         classBinding->dump(); | ||||
|     } | ||||
|  | ||||
|     --depth; | ||||
| } | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////// | ||||
| // ClassBinding | ||||
| //////////////////////////////////////////////////////////////////////////////// | ||||
| ClassBinding::ClassBinding(NamespaceBinding *parent) | ||||
|     : parent(parent) | ||||
| { | ||||
|     parent->classBindings.append(this); | ||||
| } | ||||
|  | ||||
| ClassBinding::ClassBinding(ClassBinding *parentClass) | ||||
| { | ||||
|     parent = parentClass->parent; | ||||
|     parentClass->children.append(this); | ||||
| } | ||||
|  | ||||
| ClassBinding::~ClassBinding() | ||||
| { qDeleteAll(children); } | ||||
|  | ||||
| NameId *ClassBinding::name() const | ||||
| { | ||||
|     if (symbols.size()) { | ||||
|         if (Name *name = symbols.at(0)->name()) { | ||||
|             NameId *nameId = name->asNameId(); | ||||
|             return nameId; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| Identifier *ClassBinding::identifier() const | ||||
| { | ||||
|     if (NameId *nameId = name()) | ||||
|         return nameId->identifier(); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////// | ||||
| // Binder | ||||
| //////////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| class Binder: protected SymbolVisitor | ||||
| { | ||||
| public: | ||||
|     Binder(NamespaceBinding *globals); | ||||
|     virtual ~Binder(); | ||||
|  | ||||
|     NamespaceBinding *operator()(Document::Ptr doc, const Snapshot &snapshot) | ||||
|     { | ||||
|         namespaceBinding = _globals; | ||||
|         const Snapshot previousSnapshot = _snapshot; | ||||
|  | ||||
|         _snapshot = snapshot; | ||||
|         (void) bind(doc); | ||||
|         _snapshot = previousSnapshot; | ||||
|  | ||||
|         return _globals; | ||||
|     } | ||||
|  | ||||
|     Snapshot _snapshot; | ||||
|  | ||||
| protected: | ||||
|     NamespaceBinding *bind(Document::Ptr doc) | ||||
|     { | ||||
|         QSet<QString> processed; | ||||
|         return bind(doc, &processed); | ||||
|     } | ||||
|  | ||||
|     NamespaceBinding *bind(Document::Ptr doc, QSet<QString> *processed) | ||||
|     { | ||||
|         if (processed->contains(doc->fileName())) | ||||
|             return 0; | ||||
|  | ||||
|         processed->insert(doc->fileName()); | ||||
|  | ||||
|         foreach (const Document::Include &i, doc->includes()) { | ||||
|             if (Document::Ptr includedDoc = _snapshot.value(i.fileName())) { | ||||
|                 /*NamepaceBinding *binding = */ bind(includedDoc, processed); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Namespace *ns = doc->globalNamespace(); | ||||
|         _globals->symbols.append(ns); | ||||
|  | ||||
|         for (unsigned i = 0; i < ns->memberCount(); ++i) { | ||||
|             (void) bind(ns->memberAt(i), _globals); | ||||
|         } | ||||
|  | ||||
|         return _globals; | ||||
|     } | ||||
|  | ||||
|     NamespaceBinding *bind(Symbol *symbol, NamespaceBinding *binding); | ||||
|     NamespaceBinding *findOrCreateNamespaceBinding(Namespace *symbol); | ||||
|     NamespaceBinding *resolveNamespace(const Location &loc, Name *name); | ||||
|  | ||||
|     NamespaceBinding *switchNamespaceBinding(NamespaceBinding *binding); | ||||
|  | ||||
|     ClassBinding *findOrCreateClassBinding(Class *classSymbol); | ||||
|     ClassBinding *findClassBinding(Name *name); | ||||
|  | ||||
|     ClassBinding *switchClassBinding(ClassBinding *binding); | ||||
|  | ||||
|     using SymbolVisitor::visit; | ||||
|  | ||||
|     virtual bool visit(Namespace *); | ||||
|     virtual bool visit(UsingNamespaceDirective *); | ||||
|     virtual bool visit(Class *); | ||||
|     virtual bool visit(Function *); | ||||
|     virtual bool visit(Block *); | ||||
|  | ||||
| private: | ||||
|     NamespaceBinding *_globals; | ||||
|     NamespaceBinding *namespaceBinding; | ||||
|     ClassBinding *classBinding; | ||||
| }; | ||||
|  | ||||
| Binder::Binder(NamespaceBinding *globals) | ||||
|     : _globals(globals), | ||||
|       namespaceBinding(0), | ||||
|       classBinding(0) | ||||
| { } | ||||
|  | ||||
| Binder::~Binder() | ||||
| { } | ||||
|  | ||||
| NamespaceBinding *Binder::bind(Symbol *symbol, NamespaceBinding *binding) | ||||
| { | ||||
|     NamespaceBinding *previousBinding = switchNamespaceBinding(binding); | ||||
|     accept(symbol); | ||||
|     return switchNamespaceBinding(previousBinding); | ||||
| } | ||||
|  | ||||
| NamespaceBinding *Binder::findOrCreateNamespaceBinding(Namespace *symbol) | ||||
| { return namespaceBinding->findOrCreateNamespaceBinding(symbol); } | ||||
|  | ||||
| NamespaceBinding *Binder::resolveNamespace(const Location &loc, Name *name) | ||||
| { | ||||
|     if (! namespaceBinding) | ||||
|         return 0; | ||||
|  | ||||
|     return namespaceBinding->resolveNamespace(loc, name); | ||||
| } | ||||
|  | ||||
| NamespaceBinding *Binder::switchNamespaceBinding(NamespaceBinding *binding) | ||||
| { | ||||
|     NamespaceBinding *previousBinding = namespaceBinding; | ||||
|     namespaceBinding = binding; | ||||
|     return previousBinding; | ||||
| } | ||||
|  | ||||
| ClassBinding *Binder::findOrCreateClassBinding(Class *classSymbol) | ||||
| { | ||||
|     ClassBinding *binding = 0; | ||||
|  | ||||
|     if (classBinding) | ||||
|         binding = new ClassBinding(classBinding); | ||||
|     else | ||||
|         binding = new ClassBinding(namespaceBinding); | ||||
|  | ||||
|     binding->symbols.append(classSymbol); | ||||
|     return binding; | ||||
| } | ||||
|  | ||||
| ClassBinding *Binder::findClassBinding(Name *name) | ||||
| { | ||||
|     qDebug() << Q_FUNC_INFO; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| ClassBinding *Binder::switchClassBinding(ClassBinding *binding) | ||||
| { | ||||
|     ClassBinding *previousClassBinding = classBinding; | ||||
|     classBinding = binding; | ||||
|     return previousClassBinding; | ||||
| } | ||||
|  | ||||
| bool Binder::visit(Namespace *symbol) | ||||
| { | ||||
|     NamespaceBinding *binding = findOrCreateNamespaceBinding(symbol); | ||||
|  | ||||
|     for (unsigned i = 0; i < symbol->memberCount(); ++i) { | ||||
|         Symbol *member = symbol->memberAt(i); | ||||
|  | ||||
|         bind(member, binding); | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool Binder::visit(UsingNamespaceDirective *u) | ||||
| { | ||||
|     NamespaceBinding *resolved = resolveNamespace(Location(u), u->name()); | ||||
|  | ||||
|     if (! resolved) | ||||
|         return false; | ||||
|  | ||||
|     namespaceBinding->usings.append(resolved); | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool Binder::visit(Class *classSymbol) | ||||
| { | ||||
|     Overview oo; | ||||
|  | ||||
|     ClassBinding *binding = findOrCreateClassBinding(classSymbol); | ||||
|     ClassBinding *previousClassBinding = switchClassBinding(binding); | ||||
|  | ||||
| #if 0 | ||||
|     for (unsigned i = 0; i < classSymbol->baseClassCount(); ++i) { | ||||
|         BaseClass *baseClass = classSymbol->baseClassAt(i); | ||||
|  | ||||
|         ClassBinding *baseClassBinding = findClassBinding(baseClass->name()); | ||||
|  | ||||
|         // ### wrong | ||||
|         binding->baseClassBindings.append(baseClassBinding); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     for (unsigned i = 0; i < classSymbol->memberCount(); ++i) | ||||
|         accept(classSymbol->memberAt(i)); | ||||
|  | ||||
|     (void) switchClassBinding(previousClassBinding); | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool Binder::visit(Function *) | ||||
| { return false; } | ||||
|  | ||||
| bool Binder::visit(Block *) | ||||
| { return false; } | ||||
|  | ||||
| } // end of anonymous namespace | ||||
|  | ||||
		Reference in New Issue
	
	Block a user