// TODO: 1. 分析老版本mmap的SourceBuffer。

// TODO: 2. 分vfs版本SourceBuffer。

// TODO: 3. 分析swift版本source manager和llvm。

1. 为什么需要SourceBuffer

2. mmap版本SourceBuffer

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class SourceBuffer {
 public:
  static auto CreateFromText(llvm::Twine text,
                             llvm::StringRef filename = "/text")
      -> llvm::Expected<SourceBuffer>;
  static auto CreateFromFile(llvm::StringRef filename)
      -> llvm::Expected<SourceBuffer>;

  SourceBuffer() = delete;

  SourceBuffer(const SourceBuffer&) = delete;

  SourceBuffer(SourceBuffer&& arg) noexcept;

  ~SourceBuffer();

  [[nodiscard]] auto filename() const -> llvm::StringRef { return filename_; }

  [[nodiscard]] auto text() const -> llvm::StringRef { return text_; }

 private:
  enum class ContentMode {
    Uninitialized,
    MMapped,
    Owned,
  };

  // Constructor for mmapped content.
  explicit SourceBuffer(std::string filename, llvm::StringRef text);
  // Constructor for owned content.
  explicit SourceBuffer(std::string filename, std::string text);

  ContentMode content_mode_;
  std::string filename_;
  std::string text_storage_;
  llvm::StringRef text_;
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
static auto CheckContentSize(int64_t size) -> llvm::Error {
  if (size < std::numeric_limits<int32_t>::max()) {
    return llvm::Error::success();
  }
  return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                 "Input too large!");
}

auto SourceBuffer::CreateFromText(llvm::Twine text, llvm::StringRef filename)
    -> llvm::Expected<SourceBuffer> {
  std::string buffer = text.str();
  auto size_check = CheckContentSize(buffer.size());
  if (size_check) {
    return std::move(size_check);
  }
  return SourceBuffer(filename.str(), std::move(buffer));
}

static auto ErrnoToError(int errno_value) -> llvm::Error {
  return llvm::errorCodeToError(
      std::error_code(errno_value, std::generic_category()));
}

auto SourceBuffer::CreateFromFile(llvm::StringRef filename)
    -> llvm::Expected<SourceBuffer> {
  std::string filename_str = filename.str();

  errno = 0;
  int file_descriptor = open(filename_str.c_str(), O_RDONLY);
  if (file_descriptor == -1) {
    return ErrnoToError(errno);
  }

  auto closer =
      llvm::make_scope_exit([file_descriptor] { close(file_descriptor); });

  struct stat stat_buffer = {};
  errno = 0;
  if (fstat(file_descriptor, &stat_buffer) == -1) {
    return ErrnoToError(errno);
  }

  int64_t size = stat_buffer.st_size;
  if (size == 0) {
    return SourceBuffer(std::move(filename_str), std::string());
  }
  auto size_check = CheckContentSize(size);
  if (size_check) {
    return std::move(size_check);
  }

  errno = 0;
  void* mapped_text = mmap(nullptr, size, PROT_READ, MAP_PRIVATE,
                           file_descriptor, /*offset=*/0);
  if (mapped_text == MAP_FAILED) {
    return ErrnoToError(errno);
  }

  errno = 0;
  closer.release();
  if (close(file_descriptor) == -1) {
    munmap(mapped_text, size);
    return ErrnoToError(errno);
  }

  return SourceBuffer(
      std::move(filename_str),
      llvm::StringRef(static_cast<const char*>(mapped_text), size));
}
SourceBuffer::SourceBuffer(SourceBuffer&& arg) noexcept
    : content_mode_(
          std::exchange(arg.content_mode_, ContentMode::Uninitialized)),
      filename_(std::move(arg.filename_)),
      text_storage_(std::move(arg.text_storage_)),
      text_(content_mode_ == ContentMode::Owned ? text_storage_ : arg.text_) {}

SourceBuffer::SourceBuffer(std::string filename, std::string text)
    : content_mode_(ContentMode::Owned),
      filename_(std::move(filename)),
      text_storage_(std::move(text)),
      text_(text_storage_) {}

SourceBuffer::SourceBuffer(std::string filename, llvm::StringRef text)
    : content_mode_(ContentMode::MMapped),
      filename_(std::move(filename)),
      text_(text) {
  COCKTAIL_CHECK(!text.empty())
      << "Must not have an empty text when we have mapped data from a file!";
}

SourceBuffer::~SourceBuffer() {
  if (content_mode_ == ContentMode::MMapped) {
    errno = 0;
    int result =
        munmap(const_cast<void*>(static_cast<const void*>(text_.data())),
               text_.size());
    COCKTAIL_CHECK(result != -1) << "Unmapping text failed!";
  }
}

3. llvm::vfs版本SourceBuffer

这段代码定义了一个名为SourceBuffer的类,该类表示Carbon源代码的缓冲区。以下是对这段代码的详细解释:

注释概述

  • SourceBuffer:这个类持有Carbon源代码的文本缓冲区,并使其可供Carbon编译器的其余部分使用。它拥有底层源代码文本的内存,并确保其与缓冲区对象一样长寿。

  • 来源:每个源代码文本缓冲区在概念上都是从Carbon源文件加载的,即使在构造缓冲区时直接提供。还保留并提供了应该用于该Carbon源文件的名称。

  • 内存管理:由于源代码文本的底层内存可能是从文件中读取的,我们可能希望使用像mmap这样的工具将该文件映射到内存中,所以为了避免需要为映射的文件定义复制语义,缓冲区本身是不可复制的。如果需要,我们可以在未来放宽这一限制,并增加一些实现复杂性。

类定义

  • SourceBuffer:这是主要的类定义,它代表了Carbon源代码的缓冲区。

公共成员函数

  • CreateFromFile:这是一个静态函数,用于从指定的文件名打开一个文件。如果成功,它返回一个SourceBuffer对象;如果失败,它打印一个错误并返回nullopt(即没有有效的SourceBuffer对象)。

  • 构造函数:默认构造函数被删除,这意味着你不能直接创建一个SourceBuffer对象。你必须使用上面的CreateFromFile函数或其他类似的工厂函数来创建一个SourceBuffer对象。

  • filename:这是一个常量成员函数,返回源文件的名称。

  • text:这是一个常量成员函数,返回源代码文本的引用。

私有成员

  • 私有构造函数:这个构造函数是私有的,这意味着你不能从类的外部直接调用它。它接受一个文件名和一个llvm::MemoryBuffer的唯一指针,并将它们存储在私有成员变量中。

  • filename_:这是一个私有成员变量,用于存储源文件的名称。

  • text_:这是一个私有成员变量,它是一个指向llvm::MemoryBuffer的唯一指针,用于存储源代码文本。

总结

SourceBuffer类是用于管理Carbon源代码的缓冲区的。它提供了从文件创建缓冲区的功能,并提供了访问源文件名和源代码文本的方法。为了内存管理和文件映射的方便,这个类是不可复制的。