/*************************************************************************** copyright : (C) 2006 by David Nolden email : david.nolden.kdevelop@art-master.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include "simpletypenamespace.h" #include "simpletypecachebinder.h" #include "safetycounter.h" //#define PHYSICAL_IMPORT //Necessary, because else nested members cannot search the correct scope #define PHYSICALLY_IMPORT_NAMESPACES extern SafetyCounter safetyCounter; //SimpleTypeNamespace implementation TypePointer SimpleTypeNamespace::clone() { return new SimpleTypeCachedNamespace( this ); } SimpleTypeNamespace::SimpleTypeNamespace( const TQStringList& fakeScope, const TQStringList& realScope ) : SimpleTypeImpl( fakeScope ), m_currentSlaveId(0) { ifVerbose( dbg() << "\"" << str() << "\": created namespace-proxy with real scope \"" << realScope.join( "::" ) << "\"" << endl ); SimpleType cm = SimpleType( realScope, HashedStringSet(), RepoCodeModel ); SimpleType ct = SimpleType( realScope, HashedStringSet(), RepoCatalog ); cm = SimpleType( cm->clone() ); ct = SimpleType( ct->clone() ); cm->setMasterProxy( this ); ct->setMasterProxy( this ); addImport( cm->desc() ); addImport( ct->desc() ); } SimpleTypeNamespace::SimpleTypeNamespace( const TQStringList& fakeScope ) : SimpleTypeImpl( fakeScope ), m_currentSlaveId(0) { ifVerbose( dbg() << "\"" << str() << "\": created namespace-proxy" << endl ); } SimpleTypeNamespace::SimpleTypeNamespace( SimpleTypeNamespace* ns ) : SimpleTypeImpl( ns ), m_currentSlaveId(0) { ifVerbose( dbg() << "\"" << str() << "\": cloning namespace" << endl ); m_aliases = ns->m_aliases; m_activeSlaves = ns->m_activeSlaves; m_activeSlaveGroups = ns->m_activeSlaveGroups; } void SimpleTypeNamespace::breakReferences() { m_aliases.clear(); m_activeSlaves.clear(); SimpleTypeImpl::breakReferences(); } SimpleTypeImpl::MemberInfo SimpleTypeNamespace::findMember( TypeDesc name, MemberInfo::MemberType type ) { std::set ignore; SimpleTypeImpl::MemberInfo ret = findMember( name, type, ignore ); ///chooseSpecialization( ret ); should not be necessary return ret; } TQValueList SimpleTypeNamespace::getMemberClasses( const TypeDesc& name ) { std::set ignore; return getMemberClasses( name, ignore ); } TQValueList SimpleTypeNamespace::getMemberClasses( const TypeDesc& name, std::set& ignore ) { HashedString myName = HashedString( scope().join( "::" ) +"%"+typeid( *this ).name() ); if ( ignore.find( myName ) != ignore.end() || !safetyCounter ) return TQValueList(); ignore.insert( myName ); TQValueList ret; SlaveList l = getSlaves( name.includeFiles() ); for ( SlaveList::iterator it = l.begin(); it != l.end(); ++it ) { if (( *it ).first.first.resolved() ) { SimpleTypeNamespace* ns = dynamic_cast(( *it ).first.first.resolved().data() ); if ( !ns ) { HashedString thatName = HashedString(( *it ).first.first.resolved()->scope().join( "::" ) +"%"+typeid( *( *it ).first.first.resolved() ).name() ); if ( ignore.find( thatName ) != ignore.end() ) continue; ignore.insert( thatName ); ret += ( *it ).first.first.resolved()->getMemberClasses( name ); } else { ret += ns->getMemberClasses( name, ignore ); } } } return ret; } SimpleTypeImpl::MemberInfo SimpleTypeNamespace::findMember( TypeDesc name, MemberInfo::MemberType type, std::set & ignore ) { MemberInfo mem; mem.name = ""; mem.memberType = MemberInfo::NotFound; HashedString myName = HashedString( scope().join( "::" ) +"%"+typeid( *this ).name() ); if ( ignore.find( myName ) != ignore.end() || !safetyCounter ) return mem; ignore.insert( myName ); SlaveList l = getSlaves( name.includeFiles() ); ImportList m_aliasImports; AliasMap::iterator itt = m_aliases.find( name.name() ); if ( itt != m_aliases.end() && !( *itt ).empty() ) { ifVerbose( dbg() << "\"" << str() << "\": namespace-sub-aliases \"" << name.name() << "\"" << "\" requested, locating targets" << endl ); for ( ImportList::iterator it = ( *itt ).begin(); it != ( *itt ).end(); ++it ) { if ( !( /*name.includeFiles().size() < 1 ||*/ ( *it ).files <= name.includeFiles() ) ) continue; //filter the slave by the include-files ifVerbose( dbg() << "\"" << str() << "\": namespace-sub-aliases \"" << name.name() << "\": taking target \"" << ( *it ).import.fullNameChain() << "\"" << endl ); /*TypeDesc d( (*it).import ); d.setIncludeFiles( name.includeFiles() );*/ m_aliasImports.insert( *it ); //@todo: what include-files should be used for searching the namespace? /*LocateResult l = locateDecType( d, SimpleTypeImpl::Normal, 0, SimpleTypeImpl::MemberInfo::Namespace ); if ( !l || !l->resolved() || !dynamic_cast( l->resolved().data() ) ) { ifVerbose( dbg() << "\"" << str() << "\": namespace-sub-aliases \"" << name.name() << "\" -> \"" << ( *it ).import.fullNameChain() << "\" could not be resolved" << endl ); } else { m_aliasImports.insert( Import( d.includeFiles(), l, this ) ); }*/ } } for ( SlaveList::iterator it = l.begin(); it != l.end(); ++it ) { if ( !( *it ).first.first.resolved() ) continue; if ( ignore.find( HashedString(( *it ).first.first.resolved()->scope().join( "::" ) +"%"+ typeid( *( *it ).first.first.resolved() ).name() ) ) != ignore.end() ) continue; ifVerbose( dbg() << "\"" << str() << "\": redirecting search for \"" << name.name() << "\" to \"" << ( *it ) .first.first.fullNameChain() << "\"" << endl ); if ( !( *it ).first.first.resolved() ) { ifVerbose( dbg() << "\"" << str() << "\": while search for \"" << name.name() << "\": Imported namespace \"" << ( *it ) .first.first.fullNameChain() << "\" is not resolved(should have been resolved in updateAliases)" << endl ); continue; } ifVerbose( dbg() << "\"Class-type: " << typeid( *( *it ).first.first.resolved().data() ).name() << ")" << endl ); SimpleTypeNamespace* ns = dynamic_cast(( *it ).first.first.resolved().data() ); if ( ns ) mem = ns->findMember( name , type, ignore ); else mem = ( *it ).first.first.resolved()->findMember( name, type ); if ( mem ) { if ( mem.memberType != MemberInfo::Namespace ) { #ifdef PHYSICAL_IMPORT TypePointer b = mem.build(); if ( b && !( b->parent()->masterProxy().data() == this ) ) { b = b ->clone(); //expensive, cache is not shared b->setParent( this ); mem.setBuilt( b ); } #else if( mem.memberType == MemberInfo::NestedType ) chooseSpecialization( mem ); TypePointer b = mem.build(); if( b && b->parent() && b->parent()->masterProxy().data() == this ) b->setParent( this ); #endif return mem; } else { TypePointer b = mem.build(); if ( b ) m_aliasImports.insert( Import( IncludeFiles(), b->desc(), TypePointer() ) ); else ifVerbose( dbg() << "\"" << str() << "\": found namespace \"" << name.name() << "\", but it is not resolved" << endl ); } } } if ( !m_aliasImports.empty() ) { return setupMemberInfo( name.fullNameList().join( "::" ), m_aliasImports ); } return mem; } // LocateResult SimpleTypeNamespace::locateSlave( const SlaveList::const_iterator& target, const IncludeFiles& includeFiles ) { // for( SlaveList::const_iterator it = m_activeSlaves.begin(); it != target; ++it ) { // // } // } SimpleTypeImpl::MemberInfo SimpleTypeNamespace::setupMemberInfo( const TQStringList& subName, const ImportList& imports ) { MemberInfo mem; mem.name = subName.join( "::" ); mem.memberType = MemberInfo::NotFound; TQStringList sc = scope(); sc += subName; mem.type = sc.join( "::" ); mem.memberType = MemberInfo::Namespace; mem.setBuildInfo( new NamespaceBuildInfo( sc, imports ) ); return mem; } ///This must be optimized void SimpleTypeNamespace::addAliasMap( const TypeDesc& name, const TypeDesc& alias, const IncludeFiles& files, bool recurse, bool symmetric, const TypePointer& perspective ) { Debug db; if ( !db ) { kdDebug( 9007 ) << str() << " addAliasMap: cannot add alias \"" << name.fullNameChain() << "\" -> \"" << alias.fullNameChain() << "\", recursion too deep" << endl; return ; } if ( name.next() ) kdDebug( 9007 ) << "addAliasMap warning: type-alias-name has order higher than one: " << name.fullNameChain() << ", only " << name.name() << " will be used" << endl; if ( name == alias ) return ; if ( symmetric ) addAliasMap( alias, name, files, recurse, false ); invalidateSecondaryCache(); invalidatePrimaryCache( true ); //Only not-found items are cleared updated here for performance-reasons(found items will stay cached) AliasMap::iterator it = m_aliases.find( name.name() ); if ( it == m_aliases.end() ) it = m_aliases.insert( name.name(), ImportList() ); Import a( files, alias, perspective ); std::pair< ImportList::const_iterator, ImportList::const_iterator > rng = ( *it ).equal_range( a ); while ( rng.first != rng.second ) { if ( rng.first->files == files ) return ; //The same alias, with the same files, has already been added. ++rng.first; } ( *it ).insert( a ); ifVerbose( dbg() << "\"" << str() << "\": adding namespace-alias \"" << name.name() << ( !symmetric ? "\" -> \"" : "\" = \"" ) << alias.name() << "\" files:\n[ " << files.print().c_str() << "]\n" << endl ); ifVerbose( if ( alias.resolved() ) dbg() << "Resolved type of the imported namespace: " << typeid( *alias.resolved() ).name() ); if ( name.name().isEmpty() ) { addImport( alias, files, perspective ); } } std::set SimpleTypeNamespace::updateAliases( const IncludeFiles& files/*, bool isRecursion */) { std::set possibleSlaves; if ( m_activeSlaves.empty() || !safetyCounter.ok() ) return possibleSlaves; // if( !isRecursion ) { // ///Test the cache // SlavesCache::const_iterator it = m_slavesCache.find( files ); // if( it != m_slavesCache.end() && it->second.first == m_slavesCache.size() ) return; ///The cache already contains a valid entry, and the work is done // } m_activeSlaveGroups.findGroups( files, possibleSlaves ); if( possibleSlaves.empty() ) return possibleSlaves; std::list disabled; for( std::set::const_reverse_iterator it = possibleSlaves.rbegin(); it != possibleSlaves.rend(); ++it ) { //Disable all slaves with higher ids SlaveMap::iterator current = m_activeSlaves.find( *it ); if( current == m_activeSlaves.end() ) { kdDebug( 9007 ) << "ERROR" << endl; } SlaveDesc& d( current->second ); if ( !d.first.first.resolved() ) { for( SlaveMap::const_iterator itr = current; itr != m_activeSlaves.end(); ++it ) { if( m_activeSlaveGroups.isDisabled( itr->first ) ) break; //stop searching when hitting the first disabled one(assuming that all behind are disabled too) disabled.push_back( itr->first ); m_activeSlaveGroups.disableSet( itr->first ); } TypeDesc descS = d.first.first; TypePointer p = d.second; //perspective HashedStringSet importIncludeFiles = d.first.second; if ( !p ) p = this; TypeDesc desc = p->locateDecType( descS, SimpleTypeImpl::Normal, 0, SimpleTypeImpl::MemberInfo::Namespace ); if ( !desc.resolved() ) { ///If the namespace could not be found, help out by including the include-files of the current search descS.setIncludeFiles( descS.includeFiles() + files ); desc = p->locateDecType( descS, SimpleTypeImpl::Normal, 0, SimpleTypeImpl::MemberInfo::Namespace ); } if ( desc.resolved() ) { ///If exactly the same namespace was already imported use the earlier imported instance, so they can share a single cache ///@todo make more efficient. for ( SlaveMap::const_iterator it = m_activeSlaves.begin(); it != m_activeSlaves.end(); ++it ) { if (( *it ).second.first.first.resolved() && ( *it ).second.first.first.resolved()->scope() == desc.resolved()->scope() && typeid( *( *it ).second.first.first.resolved().data() ) == typeid( desc.resolved().data() ) ) { desc.setResolved(( *it ).second.first.first.resolved() ); break; } } #ifdef PHYSICALLY_IMPORT_NAMESPACES if ( desc.resolved()->masterProxy().data() != this ) { desc.setResolved( desc.resolved()->clone() ); //expensive, cache is not shared desc.resolved()->setMasterProxy( this ); //Possible solution: don't use this, simply set the parents of all found members correctly } #endif d.first.first = desc; } } } for( std::list::const_iterator it = disabled.begin(); it != disabled.end(); ++it ) { m_activeSlaveGroups.enableSet( *it ); } return possibleSlaves; } void SimpleTypeNamespace::addAliases( TQString map, const IncludeFiles& files ) { while ( !map.isEmpty() ) { int mid = map.find( "=" ); int mid2 = map.find( "<<" ); int found = mid; int len = 1; if ( mid2 != -1 && ( mid2 < found || found == -1 ) ) { found = mid2; len = 2; } if ( found == -1 ) break; int end = map.find( ";", found + len ); if ( end == -1 ) { //break; end = map.length(); } if ( end - ( found + len ) < 0 ) break; addAliasMap( map.left( found ).stripWhiteSpace(), map.mid( found + len, end - found - len ).stripWhiteSpace(), files, true, found == mid ); map = map.mid( end + 1 ); } } void SimpleTypeNamespace::invalidatePrimaryCache( bool onlyNegative ) { //m_slavesCache.clear(); SimpleTypeImpl::invalidatePrimaryCache( onlyNegative ); } void SimpleTypeNamespace::addImport( const TypeDesc& import, const IncludeFiles& files, TypePointer perspective ) { //ifVerbose( dbg() << " if ( !perspective ) perspective = this; invalidateCache(); TypeDesc d = import; if ( d.resolved() ) { #ifdef PHYSICALLY_IMPORT_NAMESPACES if( d.resolved()->masterProxy().data() != this ) { d.setResolved( d.resolved()->clone() ); //Expensive because of lost caching, think about how necessary this is d.resolved()->setMasterProxy( this ); } #endif } m_activeSlaves[ ++m_currentSlaveId ] = std::make_pair( std::make_pair( d, files ) , perspective ); m_activeSlaveGroups.addSet( m_currentSlaveId, files ); if( d.resolved() ) ///Must be called after the above, because it may insert new slaves, and the order in m_activeSlaves MUST be preserved d.resolved()->addAliasesTo( this ); } bool SimpleTypeNamespace::hasNode() const { return true; } SimpleTypeNamespace::SlaveList SimpleTypeNamespace::getSlaves( const IncludeFiles& files ) { /* ///Test the cache SlavesCache::const_iterator it = m_slavesCache.find( files ); if( it != m_slavesCache.end() && it->second.first == m_activeSlaves.size() ) return it->second.second; ///The cache already contains a valid entry, and the work is done*/ std::set allSlaves = updateAliases( files ); SlaveList ret; #ifdef IMPORT_DEBUG for ( SlaveList::const_iterator it = m_activeSlaves.begin(); it != m_activeSlaves.end(); ++it ) { #ifdef IMPORT_DEBUG ifVerbose( dbg() << "\"" << str() << "\": Checking whether \"" << (*it).second.first.first.fullNameChain() << "\" should be imported, current include-files: " << files.print().c_str() << "\nNeeded include-files: " << (*it).second.first.second.print().c_str() << "\n"; ) #endif if ( !(( *it ).second.first.second <= files ) ) { #ifdef IMPORT_DEBUG ifVerbose( dbg() << "not imported." ); #endif continue; } #ifdef IMPORT_DEBUG ifVerbose( dbg() << "imported." << endl ); #endif ret.push_back( *it.second ); } #else ifVerbose( dbg() << str() << " getSlaves() called for \n[ " << files.print().c_str() << endl ); for( std::set::const_iterator it = allSlaves.begin(); it != allSlaves.end(); ++it ) { SlaveMap::const_iterator itr = m_activeSlaves.find( *it ); if( itr != m_activeSlaves.end() ) { ifVerbose( dbg() << str() << "getSlaves() returning " << (*itr).second.first.first.fullNameChain() << endl ); ret.push_back( (*itr).second ); } else { kdDebug( 9007 ) << "ERROR in getSlaves()"; } } #endif /*if( it == m_slavesCache.end() || it->second.first < m_activeSlaves.size() ) { m_slavesCache.insert( std::make_pair( files, std::make_pair( m_activeSlaves.size(), ret ) ) ); }*/ return ret; } //SimpleTypeNamespace::NamespaceBuildInfo implementation TypePointer SimpleTypeNamespace::NamespaceBuildInfo::build() { if ( m_built ) return m_built; m_built = new SimpleTypeCachedNamespace( m_fakeScope ); for ( ImportList::iterator it = m_imports.begin(); it != m_imports.end(); ++it ) { TypeDesc i = ( *it ).import; if ( i.resolved() ) { // i.setResolved( i.resolved()->clone() ); } (( SimpleTypeCachedNamespace* ) m_built.data() ) ->addAliasMap( TypeDesc(), i, ( *it ).files, true, false, ( *it ).perspective ); } return m_built; }