File: | tools/clang/lib/Serialization/ModuleManager.cpp |
Warning: | line 167, column 5 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===- ModuleManager.cpp - Module Manager ---------------------------------===// | |||
2 | // | |||
3 | // The LLVM Compiler Infrastructure | |||
4 | // | |||
5 | // This file is distributed under the University of Illinois Open Source | |||
6 | // License. See LICENSE.TXT for details. | |||
7 | // | |||
8 | //===----------------------------------------------------------------------===// | |||
9 | // | |||
10 | // This file defines the ModuleManager class, which manages a set of loaded | |||
11 | // modules for the ASTReader. | |||
12 | // | |||
13 | //===----------------------------------------------------------------------===// | |||
14 | ||||
15 | #include "clang/Serialization/ModuleManager.h" | |||
16 | #include "clang/Basic/FileManager.h" | |||
17 | #include "clang/Basic/LLVM.h" | |||
18 | #include "clang/Basic/MemoryBufferCache.h" | |||
19 | #include "clang/Lex/HeaderSearch.h" | |||
20 | #include "clang/Lex/ModuleMap.h" | |||
21 | #include "clang/Serialization/GlobalModuleIndex.h" | |||
22 | #include "clang/Serialization/Module.h" | |||
23 | #include "clang/Serialization/PCHContainerOperations.h" | |||
24 | #include "llvm/ADT/STLExtras.h" | |||
25 | #include "llvm/ADT/SetVector.h" | |||
26 | #include "llvm/ADT/SmallPtrSet.h" | |||
27 | #include "llvm/ADT/SmallVector.h" | |||
28 | #include "llvm/ADT/StringRef.h" | |||
29 | #include "llvm/ADT/iterator.h" | |||
30 | #include "llvm/Support/Chrono.h" | |||
31 | #include "llvm/Support/DOTGraphTraits.h" | |||
32 | #include "llvm/Support/ErrorOr.h" | |||
33 | #include "llvm/Support/GraphWriter.h" | |||
34 | #include "llvm/Support/MemoryBuffer.h" | |||
35 | #include "llvm/Support/VirtualFileSystem.h" | |||
36 | #include <algorithm> | |||
37 | #include <cassert> | |||
38 | #include <memory> | |||
39 | #include <string> | |||
40 | #include <system_error> | |||
41 | ||||
42 | using namespace clang; | |||
43 | using namespace serialization; | |||
44 | ||||
45 | ModuleFile *ModuleManager::lookupByFileName(StringRef Name) const { | |||
46 | const FileEntry *Entry = FileMgr.getFile(Name, /*openFile=*/false, | |||
47 | /*cacheFailure=*/false); | |||
48 | if (Entry) | |||
49 | return lookup(Entry); | |||
50 | ||||
51 | return nullptr; | |||
52 | } | |||
53 | ||||
54 | ModuleFile *ModuleManager::lookupByModuleName(StringRef Name) const { | |||
55 | if (const Module *Mod = HeaderSearchInfo.getModuleMap().findModule(Name)) | |||
56 | if (const FileEntry *File = Mod->getASTFile()) | |||
57 | return lookup(File); | |||
58 | ||||
59 | return nullptr; | |||
60 | } | |||
61 | ||||
62 | ModuleFile *ModuleManager::lookup(const FileEntry *File) const { | |||
63 | auto Known = Modules.find(File); | |||
64 | if (Known == Modules.end()) | |||
65 | return nullptr; | |||
66 | ||||
67 | return Known->second; | |||
68 | } | |||
69 | ||||
70 | std::unique_ptr<llvm::MemoryBuffer> | |||
71 | ModuleManager::lookupBuffer(StringRef Name) { | |||
72 | const FileEntry *Entry = FileMgr.getFile(Name, /*openFile=*/false, | |||
73 | /*cacheFailure=*/false); | |||
74 | return std::move(InMemoryBuffers[Entry]); | |||
75 | } | |||
76 | ||||
77 | static bool checkSignature(ASTFileSignature Signature, | |||
78 | ASTFileSignature ExpectedSignature, | |||
79 | std::string &ErrorStr) { | |||
80 | if (!ExpectedSignature || Signature == ExpectedSignature) | |||
81 | return false; | |||
82 | ||||
83 | ErrorStr = | |||
84 | Signature ? "signature mismatch" : "could not read module signature"; | |||
85 | return true; | |||
86 | } | |||
87 | ||||
88 | static void updateModuleImports(ModuleFile &MF, ModuleFile *ImportedBy, | |||
89 | SourceLocation ImportLoc) { | |||
90 | if (ImportedBy) { | |||
91 | MF.ImportedBy.insert(ImportedBy); | |||
92 | ImportedBy->Imports.insert(&MF); | |||
93 | } else { | |||
94 | if (!MF.DirectlyImported) | |||
95 | MF.ImportLoc = ImportLoc; | |||
96 | ||||
97 | MF.DirectlyImported = true; | |||
98 | } | |||
99 | } | |||
100 | ||||
101 | ModuleManager::AddModuleResult | |||
102 | ModuleManager::addModule(StringRef FileName, ModuleKind Type, | |||
103 | SourceLocation ImportLoc, ModuleFile *ImportedBy, | |||
104 | unsigned Generation, | |||
105 | off_t ExpectedSize, time_t ExpectedModTime, | |||
106 | ASTFileSignature ExpectedSignature, | |||
107 | ASTFileSignatureReader ReadSignature, | |||
108 | ModuleFile *&Module, | |||
109 | std::string &ErrorStr) { | |||
110 | Module = nullptr; | |||
111 | ||||
112 | // Look for the file entry. This only fails if the expected size or | |||
113 | // modification time differ. | |||
114 | const FileEntry *Entry; | |||
115 | if (Type == MK_ExplicitModule || Type == MK_PrebuiltModule) { | |||
| ||||
116 | // If we're not expecting to pull this file out of the module cache, it | |||
117 | // might have a different mtime due to being moved across filesystems in | |||
118 | // a distributed build. The size must still match, though. (As must the | |||
119 | // contents, but we can't check that.) | |||
120 | ExpectedModTime = 0; | |||
121 | } | |||
122 | if (lookupModuleFile(FileName, ExpectedSize, ExpectedModTime, Entry)) { | |||
123 | ErrorStr = "module file out of date"; | |||
124 | return OutOfDate; | |||
125 | } | |||
126 | ||||
127 | if (!Entry && FileName != "-") { | |||
128 | ErrorStr = "module file not found"; | |||
129 | return Missing; | |||
130 | } | |||
131 | ||||
132 | // Check whether we already loaded this module, before | |||
133 | if (ModuleFile *ModuleEntry = Modules.lookup(Entry)) { | |||
134 | // Check the stored signature. | |||
135 | if (checkSignature(ModuleEntry->Signature, ExpectedSignature, ErrorStr)) | |||
136 | return OutOfDate; | |||
137 | ||||
138 | Module = ModuleEntry; | |||
139 | updateModuleImports(*ModuleEntry, ImportedBy, ImportLoc); | |||
140 | return AlreadyLoaded; | |||
141 | } | |||
142 | ||||
143 | // Allocate a new module. | |||
144 | auto NewModule = llvm::make_unique<ModuleFile>(Type, Generation); | |||
145 | NewModule->Index = Chain.size(); | |||
146 | NewModule->FileName = FileName.str(); | |||
147 | NewModule->File = Entry; | |||
148 | NewModule->ImportLoc = ImportLoc; | |||
149 | NewModule->InputFilesValidationTimestamp = 0; | |||
150 | ||||
151 | if (NewModule->Kind == MK_ImplicitModule) { | |||
152 | std::string TimestampFilename = NewModule->getTimestampFilename(); | |||
153 | llvm::vfs::Status Status; | |||
154 | // A cached stat value would be fine as well. | |||
155 | if (!FileMgr.getNoncachedStatValue(TimestampFilename, Status)) | |||
156 | NewModule->InputFilesValidationTimestamp = | |||
157 | llvm::sys::toTimeT(Status.getLastModificationTime()); | |||
158 | } | |||
159 | ||||
160 | // Load the contents of the module | |||
161 | if (std::unique_ptr<llvm::MemoryBuffer> Buffer = lookupBuffer(FileName)) { | |||
162 | // The buffer was already provided for us. | |||
163 | NewModule->Buffer = &PCMCache->addBuffer(FileName, std::move(Buffer)); | |||
164 | // Since the cached buffer is reused, it is safe to close the file | |||
165 | // descriptor that was opened while stat()ing the PCM in | |||
166 | // lookupModuleFile() above, it won't be needed any longer. | |||
167 | Entry->closeFile(); | |||
| ||||
168 | } else if (llvm::MemoryBuffer *Buffer = PCMCache->lookupBuffer(FileName)) { | |||
169 | NewModule->Buffer = Buffer; | |||
170 | // As above, the file descriptor is no longer needed. | |||
171 | Entry->closeFile(); | |||
172 | } else { | |||
173 | // Open the AST file. | |||
174 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buf((std::error_code())); | |||
175 | if (FileName == "-") { | |||
176 | Buf = llvm::MemoryBuffer::getSTDIN(); | |||
177 | } else { | |||
178 | // Get a buffer of the file and close the file descriptor when done. | |||
179 | Buf = FileMgr.getBufferForFile(NewModule->File, | |||
180 | /*IsVolatile=*/false, | |||
181 | /*ShouldClose=*/true); | |||
182 | } | |||
183 | ||||
184 | if (!Buf) { | |||
185 | ErrorStr = Buf.getError().message(); | |||
186 | return Missing; | |||
187 | } | |||
188 | ||||
189 | NewModule->Buffer = &PCMCache->addBuffer(FileName, std::move(*Buf)); | |||
190 | } | |||
191 | ||||
192 | // Initialize the stream. | |||
193 | NewModule->Data = PCHContainerRdr.ExtractPCH(*NewModule->Buffer); | |||
194 | ||||
195 | // Read the signature eagerly now so that we can check it. Avoid calling | |||
196 | // ReadSignature unless there's something to check though. | |||
197 | if (ExpectedSignature && checkSignature(ReadSignature(NewModule->Data), | |||
198 | ExpectedSignature, ErrorStr)) { | |||
199 | // Try to remove the buffer. If it can't be removed, then it was already | |||
200 | // validated by this process. | |||
201 | if (!PCMCache->tryToRemoveBuffer(NewModule->FileName)) | |||
202 | FileMgr.invalidateCache(NewModule->File); | |||
203 | return OutOfDate; | |||
204 | } | |||
205 | ||||
206 | // We're keeping this module. Store it everywhere. | |||
207 | Module = Modules[Entry] = NewModule.get(); | |||
208 | ||||
209 | updateModuleImports(*NewModule, ImportedBy, ImportLoc); | |||
210 | ||||
211 | if (!NewModule->isModule()) | |||
212 | PCHChain.push_back(NewModule.get()); | |||
213 | if (!ImportedBy) | |||
214 | Roots.push_back(NewModule.get()); | |||
215 | ||||
216 | Chain.push_back(std::move(NewModule)); | |||
217 | return NewlyLoaded; | |||
218 | } | |||
219 | ||||
220 | void ModuleManager::removeModules( | |||
221 | ModuleIterator First, | |||
222 | llvm::SmallPtrSetImpl<ModuleFile *> &LoadedSuccessfully, | |||
223 | ModuleMap *modMap) { | |||
224 | auto Last = end(); | |||
225 | if (First == Last) | |||
226 | return; | |||
227 | ||||
228 | // Explicitly clear VisitOrder since we might not notice it is stale. | |||
229 | VisitOrder.clear(); | |||
230 | ||||
231 | // Collect the set of module file pointers that we'll be removing. | |||
232 | llvm::SmallPtrSet<ModuleFile *, 4> victimSet( | |||
233 | (llvm::pointer_iterator<ModuleIterator>(First)), | |||
234 | (llvm::pointer_iterator<ModuleIterator>(Last))); | |||
235 | ||||
236 | auto IsVictim = [&](ModuleFile *MF) { | |||
237 | return victimSet.count(MF); | |||
238 | }; | |||
239 | // Remove any references to the now-destroyed modules. | |||
240 | for (auto I = begin(); I != First; ++I) { | |||
241 | I->Imports.remove_if(IsVictim); | |||
242 | I->ImportedBy.remove_if(IsVictim); | |||
243 | } | |||
244 | Roots.erase(std::remove_if(Roots.begin(), Roots.end(), IsVictim), | |||
245 | Roots.end()); | |||
246 | ||||
247 | // Remove the modules from the PCH chain. | |||
248 | for (auto I = First; I != Last; ++I) { | |||
249 | if (!I->isModule()) { | |||
250 | PCHChain.erase(std::find(PCHChain.begin(), PCHChain.end(), &*I), | |||
251 | PCHChain.end()); | |||
252 | break; | |||
253 | } | |||
254 | } | |||
255 | ||||
256 | // Delete the modules and erase them from the various structures. | |||
257 | for (ModuleIterator victim = First; victim != Last; ++victim) { | |||
258 | Modules.erase(victim->File); | |||
259 | ||||
260 | if (modMap) { | |||
261 | StringRef ModuleName = victim->ModuleName; | |||
262 | if (Module *mod = modMap->findModule(ModuleName)) { | |||
263 | mod->setASTFile(nullptr); | |||
264 | } | |||
265 | } | |||
266 | ||||
267 | // Files that didn't make it through ReadASTCore successfully will be | |||
268 | // rebuilt (or there was an error). Invalidate them so that we can load the | |||
269 | // new files that will be renamed over the old ones. | |||
270 | // | |||
271 | // The PCMCache tracks whether the module was successfully loaded in another | |||
272 | // thread/context; in that case, it won't need to be rebuilt (and we can't | |||
273 | // safely invalidate it anyway). | |||
274 | if (LoadedSuccessfully.count(&*victim) == 0 && | |||
275 | !PCMCache->tryToRemoveBuffer(victim->FileName)) | |||
276 | FileMgr.invalidateCache(victim->File); | |||
277 | } | |||
278 | ||||
279 | // Delete the modules. | |||
280 | Chain.erase(Chain.begin() + (First - begin()), Chain.end()); | |||
281 | } | |||
282 | ||||
283 | void | |||
284 | ModuleManager::addInMemoryBuffer(StringRef FileName, | |||
285 | std::unique_ptr<llvm::MemoryBuffer> Buffer) { | |||
286 | const FileEntry *Entry = | |||
287 | FileMgr.getVirtualFile(FileName, Buffer->getBufferSize(), 0); | |||
288 | InMemoryBuffers[Entry] = std::move(Buffer); | |||
289 | } | |||
290 | ||||
291 | ModuleManager::VisitState *ModuleManager::allocateVisitState() { | |||
292 | // Fast path: if we have a cached state, use it. | |||
293 | if (FirstVisitState) { | |||
294 | VisitState *Result = FirstVisitState; | |||
295 | FirstVisitState = FirstVisitState->NextState; | |||
296 | Result->NextState = nullptr; | |||
297 | return Result; | |||
298 | } | |||
299 | ||||
300 | // Allocate and return a new state. | |||
301 | return new VisitState(size()); | |||
302 | } | |||
303 | ||||
304 | void ModuleManager::returnVisitState(VisitState *State) { | |||
305 | assert(State->NextState == nullptr && "Visited state is in list?")((State->NextState == nullptr && "Visited state is in list?" ) ? static_cast<void> (0) : __assert_fail ("State->NextState == nullptr && \"Visited state is in list?\"" , "/build/llvm-toolchain-snapshot-8~svn350071/tools/clang/lib/Serialization/ModuleManager.cpp" , 305, __PRETTY_FUNCTION__)); | |||
306 | State->NextState = FirstVisitState; | |||
307 | FirstVisitState = State; | |||
308 | } | |||
309 | ||||
310 | void ModuleManager::setGlobalIndex(GlobalModuleIndex *Index) { | |||
311 | GlobalIndex = Index; | |||
312 | if (!GlobalIndex) { | |||
313 | ModulesInCommonWithGlobalIndex.clear(); | |||
314 | return; | |||
315 | } | |||
316 | ||||
317 | // Notify the global module index about all of the modules we've already | |||
318 | // loaded. | |||
319 | for (ModuleFile &M : *this) | |||
320 | if (!GlobalIndex->loadedModuleFile(&M)) | |||
321 | ModulesInCommonWithGlobalIndex.push_back(&M); | |||
322 | } | |||
323 | ||||
324 | void ModuleManager::moduleFileAccepted(ModuleFile *MF) { | |||
325 | if (!GlobalIndex || GlobalIndex->loadedModuleFile(MF)) | |||
326 | return; | |||
327 | ||||
328 | ModulesInCommonWithGlobalIndex.push_back(MF); | |||
329 | } | |||
330 | ||||
331 | ModuleManager::ModuleManager(FileManager &FileMgr, MemoryBufferCache &PCMCache, | |||
332 | const PCHContainerReader &PCHContainerRdr, | |||
333 | const HeaderSearch& HeaderSearchInfo) | |||
334 | : FileMgr(FileMgr), PCMCache(&PCMCache), PCHContainerRdr(PCHContainerRdr), | |||
335 | HeaderSearchInfo(HeaderSearchInfo) {} | |||
336 | ||||
337 | ModuleManager::~ModuleManager() { delete FirstVisitState; } | |||
338 | ||||
339 | void ModuleManager::visit(llvm::function_ref<bool(ModuleFile &M)> Visitor, | |||
340 | llvm::SmallPtrSetImpl<ModuleFile *> *ModuleFilesHit) { | |||
341 | // If the visitation order vector is the wrong size, recompute the order. | |||
342 | if (VisitOrder.size() != Chain.size()) { | |||
343 | unsigned N = size(); | |||
344 | VisitOrder.clear(); | |||
345 | VisitOrder.reserve(N); | |||
346 | ||||
347 | // Record the number of incoming edges for each module. When we | |||
348 | // encounter a module with no incoming edges, push it into the queue | |||
349 | // to seed the queue. | |||
350 | SmallVector<ModuleFile *, 4> Queue; | |||
351 | Queue.reserve(N); | |||
352 | llvm::SmallVector<unsigned, 4> UnusedIncomingEdges; | |||
353 | UnusedIncomingEdges.resize(size()); | |||
354 | for (ModuleFile &M : llvm::reverse(*this)) { | |||
355 | unsigned Size = M.ImportedBy.size(); | |||
356 | UnusedIncomingEdges[M.Index] = Size; | |||
357 | if (!Size) | |||
358 | Queue.push_back(&M); | |||
359 | } | |||
360 | ||||
361 | // Traverse the graph, making sure to visit a module before visiting any | |||
362 | // of its dependencies. | |||
363 | while (!Queue.empty()) { | |||
364 | ModuleFile *CurrentModule = Queue.pop_back_val(); | |||
365 | VisitOrder.push_back(CurrentModule); | |||
366 | ||||
367 | // For any module that this module depends on, push it on the | |||
368 | // stack (if it hasn't already been marked as visited). | |||
369 | for (auto M = CurrentModule->Imports.rbegin(), | |||
370 | MEnd = CurrentModule->Imports.rend(); | |||
371 | M != MEnd; ++M) { | |||
372 | // Remove our current module as an impediment to visiting the | |||
373 | // module we depend on. If we were the last unvisited module | |||
374 | // that depends on this particular module, push it into the | |||
375 | // queue to be visited. | |||
376 | unsigned &NumUnusedEdges = UnusedIncomingEdges[(*M)->Index]; | |||
377 | if (NumUnusedEdges && (--NumUnusedEdges == 0)) | |||
378 | Queue.push_back(*M); | |||
379 | } | |||
380 | } | |||
381 | ||||
382 | assert(VisitOrder.size() == N && "Visitation order is wrong?")((VisitOrder.size() == N && "Visitation order is wrong?" ) ? static_cast<void> (0) : __assert_fail ("VisitOrder.size() == N && \"Visitation order is wrong?\"" , "/build/llvm-toolchain-snapshot-8~svn350071/tools/clang/lib/Serialization/ModuleManager.cpp" , 382, __PRETTY_FUNCTION__)); | |||
383 | ||||
384 | delete FirstVisitState; | |||
385 | FirstVisitState = nullptr; | |||
386 | } | |||
387 | ||||
388 | VisitState *State = allocateVisitState(); | |||
389 | unsigned VisitNumber = State->NextVisitNumber++; | |||
390 | ||||
391 | // If the caller has provided us with a hit-set that came from the global | |||
392 | // module index, mark every module file in common with the global module | |||
393 | // index that is *not* in that set as 'visited'. | |||
394 | if (ModuleFilesHit && !ModulesInCommonWithGlobalIndex.empty()) { | |||
395 | for (unsigned I = 0, N = ModulesInCommonWithGlobalIndex.size(); I != N; ++I) | |||
396 | { | |||
397 | ModuleFile *M = ModulesInCommonWithGlobalIndex[I]; | |||
398 | if (!ModuleFilesHit->count(M)) | |||
399 | State->VisitNumber[M->Index] = VisitNumber; | |||
400 | } | |||
401 | } | |||
402 | ||||
403 | for (unsigned I = 0, N = VisitOrder.size(); I != N; ++I) { | |||
404 | ModuleFile *CurrentModule = VisitOrder[I]; | |||
405 | // Should we skip this module file? | |||
406 | if (State->VisitNumber[CurrentModule->Index] == VisitNumber) | |||
407 | continue; | |||
408 | ||||
409 | // Visit the module. | |||
410 | assert(State->VisitNumber[CurrentModule->Index] == VisitNumber - 1)((State->VisitNumber[CurrentModule->Index] == VisitNumber - 1) ? static_cast<void> (0) : __assert_fail ("State->VisitNumber[CurrentModule->Index] == VisitNumber - 1" , "/build/llvm-toolchain-snapshot-8~svn350071/tools/clang/lib/Serialization/ModuleManager.cpp" , 410, __PRETTY_FUNCTION__)); | |||
411 | State->VisitNumber[CurrentModule->Index] = VisitNumber; | |||
412 | if (!Visitor(*CurrentModule)) | |||
413 | continue; | |||
414 | ||||
415 | // The visitor has requested that cut off visitation of any | |||
416 | // module that the current module depends on. To indicate this | |||
417 | // behavior, we mark all of the reachable modules as having been visited. | |||
418 | ModuleFile *NextModule = CurrentModule; | |||
419 | do { | |||
420 | // For any module that this module depends on, push it on the | |||
421 | // stack (if it hasn't already been marked as visited). | |||
422 | for (llvm::SetVector<ModuleFile *>::iterator | |||
423 | M = NextModule->Imports.begin(), | |||
424 | MEnd = NextModule->Imports.end(); | |||
425 | M != MEnd; ++M) { | |||
426 | if (State->VisitNumber[(*M)->Index] != VisitNumber) { | |||
427 | State->Stack.push_back(*M); | |||
428 | State->VisitNumber[(*M)->Index] = VisitNumber; | |||
429 | } | |||
430 | } | |||
431 | ||||
432 | if (State->Stack.empty()) | |||
433 | break; | |||
434 | ||||
435 | // Pop the next module off the stack. | |||
436 | NextModule = State->Stack.pop_back_val(); | |||
437 | } while (true); | |||
438 | } | |||
439 | ||||
440 | returnVisitState(State); | |||
441 | } | |||
442 | ||||
443 | bool ModuleManager::lookupModuleFile(StringRef FileName, | |||
444 | off_t ExpectedSize, | |||
445 | time_t ExpectedModTime, | |||
446 | const FileEntry *&File) { | |||
447 | if (FileName == "-") { | |||
448 | File = nullptr; | |||
449 | return false; | |||
450 | } | |||
451 | ||||
452 | // Open the file immediately to ensure there is no race between stat'ing and | |||
453 | // opening the file. | |||
454 | File = FileMgr.getFile(FileName, /*openFile=*/true, /*cacheFailure=*/false); | |||
455 | if (!File) | |||
456 | return false; | |||
457 | ||||
458 | if ((ExpectedSize && ExpectedSize != File->getSize()) || | |||
459 | (ExpectedModTime && ExpectedModTime != File->getModificationTime())) | |||
460 | // Do not destroy File, as it may be referenced. If we need to rebuild it, | |||
461 | // it will be destroyed by removeModules. | |||
462 | return true; | |||
463 | ||||
464 | return false; | |||
465 | } | |||
466 | ||||
467 | #ifndef NDEBUG | |||
468 | namespace llvm { | |||
469 | ||||
470 | template<> | |||
471 | struct GraphTraits<ModuleManager> { | |||
472 | using NodeRef = ModuleFile *; | |||
473 | using ChildIteratorType = llvm::SetVector<ModuleFile *>::const_iterator; | |||
474 | using nodes_iterator = pointer_iterator<ModuleManager::ModuleConstIterator>; | |||
475 | ||||
476 | static ChildIteratorType child_begin(NodeRef Node) { | |||
477 | return Node->Imports.begin(); | |||
478 | } | |||
479 | ||||
480 | static ChildIteratorType child_end(NodeRef Node) { | |||
481 | return Node->Imports.end(); | |||
482 | } | |||
483 | ||||
484 | static nodes_iterator nodes_begin(const ModuleManager &Manager) { | |||
485 | return nodes_iterator(Manager.begin()); | |||
486 | } | |||
487 | ||||
488 | static nodes_iterator nodes_end(const ModuleManager &Manager) { | |||
489 | return nodes_iterator(Manager.end()); | |||
490 | } | |||
491 | }; | |||
492 | ||||
493 | template<> | |||
494 | struct DOTGraphTraits<ModuleManager> : public DefaultDOTGraphTraits { | |||
495 | explicit DOTGraphTraits(bool IsSimple = false) | |||
496 | : DefaultDOTGraphTraits(IsSimple) {} | |||
497 | ||||
498 | static bool renderGraphFromBottomUp() { return true; } | |||
499 | ||||
500 | std::string getNodeLabel(ModuleFile *M, const ModuleManager&) { | |||
501 | return M->ModuleName; | |||
502 | } | |||
503 | }; | |||
504 | ||||
505 | } // namespace llvm | |||
506 | ||||
507 | void ModuleManager::viewGraph() { | |||
508 | llvm::ViewGraph(*this, "Modules"); | |||
509 | } | |||
510 | #endif |