|
10 | 10 | //
|
11 | 11 | //===----------------------------------------------------------------------===//
|
12 | 12 |
|
| 13 | +#include "swift/Basic/Range.h" |
13 | 14 | #include "swift/Basic/SourceLoc.h"
|
14 | 15 | #include "swift/Basic/SourceManager.h"
|
15 | 16 | #include "llvm/Support/FileSystem.h"
|
@@ -407,21 +408,119 @@ SourceManager::getGeneratedSourceInfo(unsigned bufferID) const {
|
407 | 408 | return known->second;
|
408 | 409 | }
|
409 | 410 |
|
| 411 | +namespace { |
| 412 | + /// Compare the source location ranges for two buffers, as an ordering to |
| 413 | + /// use for fast searches. |
| 414 | + struct BufferIDRangeComparison { |
| 415 | + const SourceManager *sourceMgr; |
| 416 | + |
| 417 | + bool operator()(unsigned lhsID, unsigned rhsID) const { |
| 418 | + auto lhsRange = sourceMgr->getRangeForBuffer(lhsID); |
| 419 | + auto rhsRange = sourceMgr->getRangeForBuffer(rhsID); |
| 420 | + |
| 421 | + // If the source buffers are identical, we want the higher-numbered |
| 422 | + // source buffers to occur first. This is important when uniquing. |
| 423 | + if (lhsRange == rhsRange) |
| 424 | + return lhsID > rhsID; |
| 425 | + |
| 426 | + std::less<const char *> pointerCompare; |
| 427 | + return pointerCompare( |
| 428 | + (const char *)lhsRange.getStart().getOpaquePointerValue(), |
| 429 | + (const char *)rhsRange.getStart().getOpaquePointerValue()); |
| 430 | + } |
| 431 | + |
| 432 | + bool operator()(unsigned lhsID, SourceLoc rhsLoc) const { |
| 433 | + auto lhsRange = sourceMgr->getRangeForBuffer(lhsID); |
| 434 | + |
| 435 | + std::less<const char *> pointerCompare; |
| 436 | + return pointerCompare( |
| 437 | + (const char *)lhsRange.getEnd().getOpaquePointerValue(), |
| 438 | + (const char *)rhsLoc.getOpaquePointerValue()); |
| 439 | + } |
| 440 | + |
| 441 | + bool operator()(SourceLoc lhsLoc, unsigned rhsID) const { |
| 442 | + auto rhsRange = sourceMgr->getRangeForBuffer(rhsID); |
| 443 | + |
| 444 | + std::less<const char *> pointerCompare; |
| 445 | + return pointerCompare( |
| 446 | + (const char *)lhsLoc.getOpaquePointerValue(), |
| 447 | + (const char *)rhsRange.getEnd().getOpaquePointerValue()); |
| 448 | + } |
| 449 | + }; |
| 450 | + |
| 451 | + /// Determine whether the source ranges for two buffers are equivalent. |
| 452 | + struct BufferIDSameRange { |
| 453 | + const SourceManager *sourceMgr; |
| 454 | + |
| 455 | + bool operator()(unsigned lhsID, unsigned rhsID) const { |
| 456 | + auto lhsRange = sourceMgr->getRangeForBuffer(lhsID); |
| 457 | + auto rhsRange = sourceMgr->getRangeForBuffer(rhsID); |
| 458 | + |
| 459 | + return lhsRange == rhsRange; |
| 460 | + } |
| 461 | + }; |
| 462 | +} |
| 463 | + |
410 | 464 | llvm::Optional<unsigned>
|
411 | 465 | SourceManager::findBufferContainingLocInternal(SourceLoc Loc) const {
|
412 | 466 | assert(Loc.isValid());
|
413 |
| - // Search the buffers back-to front, so later alias buffers are |
414 |
| - // visited first. |
415 |
| - auto less_equal = std::less_equal<const char *>(); |
416 |
| - for (unsigned i = LLVMSourceMgr.getNumBuffers(), e = 1; i >= e; --i) { |
417 |
| - auto Buf = LLVMSourceMgr.getMemoryBuffer(i); |
418 |
| - if (less_equal(Buf->getBufferStart(), Loc.Value.getPointer()) && |
| 467 | + |
| 468 | + // If the cache is out-of-date, update it now. |
| 469 | + unsigned numBuffers = LLVMSourceMgr.getNumBuffers(); |
| 470 | + if (numBuffers != LocCache.numBuffersOriginal) { |
| 471 | + LocCache.sortedBuffers.assign( |
| 472 | + std::begin(range(1, numBuffers+1)), std::end(range(1, numBuffers+1))); |
| 473 | + LocCache.numBuffersOriginal = numBuffers; |
| 474 | + |
| 475 | + // Sort the buffer IDs by source range. |
| 476 | + std::sort(LocCache.sortedBuffers.begin(), |
| 477 | + LocCache.sortedBuffers.end(), |
| 478 | + BufferIDRangeComparison{this}); |
| 479 | + |
| 480 | + // Remove lower-numbered buffers with the same source ranges as higher- |
| 481 | + // numbered buffers. We want later alias buffers to be found first. |
| 482 | + auto newEnd = std::unique( |
| 483 | + LocCache.sortedBuffers.begin(), LocCache.sortedBuffers.end(), |
| 484 | + BufferIDSameRange{this}); |
| 485 | + LocCache.sortedBuffers.erase(newEnd, LocCache.sortedBuffers.end()); |
| 486 | + |
| 487 | + // Forget the last buffer we looked at; it might have been replaced. |
| 488 | + LocCache.lastBufferID = llvm::None; |
| 489 | + } |
| 490 | + |
| 491 | + // Determine whether the source location we're looking for is within the |
| 492 | + // given buffer ID. |
| 493 | + auto isInBuffer = [&](unsigned bufferID) { |
| 494 | + auto less_equal = std::less_equal<const char *>(); |
| 495 | + auto buffer = LLVMSourceMgr.getMemoryBuffer(bufferID); |
| 496 | + |
| 497 | + return less_equal(buffer->getBufferStart(), Loc.Value.getPointer()) && |
419 | 498 | // Use <= here so that a pointer to the null at the end of the buffer
|
420 | 499 | // is included as part of the buffer.
|
421 |
| - less_equal(Loc.Value.getPointer(), Buf->getBufferEnd())) |
422 |
| - return i; |
| 500 | + less_equal(Loc.Value.getPointer(), buffer->getBufferEnd()); |
| 501 | + }; |
| 502 | + |
| 503 | + // Check the last buffer we looked in. |
| 504 | + if (auto lastBufferID = LocCache.lastBufferID) { |
| 505 | + if (isInBuffer(*lastBufferID)) |
| 506 | + return *lastBufferID; |
423 | 507 | }
|
424 |
| - return llvm::None; |
| 508 | + |
| 509 | + // Search the sorted list of buffer IDs. |
| 510 | + auto found = std::lower_bound(LocCache.sortedBuffers.begin(), |
| 511 | + LocCache.sortedBuffers.end(), |
| 512 | + Loc, |
| 513 | + BufferIDRangeComparison{this}); |
| 514 | + |
| 515 | + // If the location was past the range covered by source buffers or |
| 516 | + // is not within any of the source buffers, fail. |
| 517 | + if (found == LocCache.sortedBuffers.end() || !isInBuffer(*found)) |
| 518 | + return llvm::None; |
| 519 | + |
| 520 | + // Cache the buffer ID we just found, because the next location is likely to |
| 521 | + // be close by. |
| 522 | + LocCache.lastBufferID = *found; |
| 523 | + return *found; |
425 | 524 | }
|
426 | 525 |
|
427 | 526 | unsigned SourceManager::findBufferContainingLoc(SourceLoc Loc) const {
|
|
0 commit comments