Flutter Engine
file_loader.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
7 #include <iostream>
8 #include <memory>
9 #include <utility>
10 
11 #include "tonic/common/macros.h"
17 
18 namespace tonic {
19 namespace {
20 
21 constexpr char kDartScheme[] = "dart:";
22 
23 constexpr char kFileScheme[] = "file:";
24 constexpr size_t kFileSchemeLength = sizeof(kFileScheme) - 1;
25 
26 constexpr char kPackageScheme[] = "package:";
27 constexpr size_t kPackageSchemeLength = sizeof(kPackageScheme) - 1;
28 
29 // Extract the scheme prefix ('package:' or 'file:' from )
30 std::string ExtractSchemePrefix(std::string url) {
31  if (url.find(kPackageScheme) == 0u)
32  return kPackageScheme;
33  if (url.find(kFileScheme) == 0u)
34  return kFileScheme;
35  return std::string();
36 }
37 
38 // Extract the path from a package: or file: url.
39 std::string ExtractPath(std::string url) {
40  if (url.find(kPackageScheme) == 0u)
41  return url.substr(kPackageSchemeLength);
42  if (url.find(kFileScheme) == 0u)
43  return url.substr(kFileSchemeLength);
44  return url;
45 }
46 
47 } // namespace
48 
49 FileLoader::FileLoader(int dirfd) : dirfd_(dirfd) {}
50 
52  for (auto kernel_buffer : kernel_buffers_)
53  free(kernel_buffer);
54 
55  if (dirfd_ >= 0)
56  close(dirfd_);
57 }
58 
59 std::string FileLoader::SanitizeURIEscapedCharacters(const std::string& str) {
60  std::string result;
61  result.reserve(str.size());
62  for (std::string::size_type i = 0; i < str.size(); ++i) {
63  if (str[i] == '%') {
64  if (i > str.size() - 3 || !isxdigit(str[i + 1]) || !isxdigit(str[i + 2]))
65  return "";
66  const std::string hex = str.substr(i + 1, 2);
67  const unsigned char c = strtoul(hex.c_str(), nullptr, 16);
68  if (!c)
69  return "";
70  result += c;
71  i += 2;
72  } else {
73  result += str[i];
74  }
75  }
76  return result;
77 }
78 
79 bool FileLoader::LoadPackagesMap(const std::string& packages) {
80  packages_ = packages;
81  std::string packages_source;
82  if (!ReadFileToString(packages_, &packages_source)) {
83  tonic::Log("error: Unable to load .packages file '%s'.", packages_.c_str());
84  return false;
85  }
86  packages_map_.reset(new PackagesMap());
87  std::string error;
88  if (!packages_map_->Parse(packages_source, &error)) {
89  tonic::Log("error: Unable to parse .packages file '%s'. %s",
90  packages_.c_str(), error.c_str());
91  return false;
92  }
93  return true;
94 }
95 
96 std::string FileLoader::GetFilePathForPackageURL(std::string url) {
97  if (!packages_map_)
98  return std::string();
99  TONIC_DCHECK(url.find(kPackageScheme) == 0u);
100  url = url.substr(kPackageSchemeLength);
101 
102  size_t slash = url.find(FileLoader::kPathSeparator);
103  if (slash == std::string::npos)
104  return std::string();
105  std::string package = url.substr(0, slash);
106  std::string library_path = url.substr(slash + 1);
107  std::string package_path = packages_map_->Resolve(package);
108  if (package_path.empty())
109  return std::string();
110  if (package_path.find(FileLoader::kFileURLPrefix) == 0u)
111  return SanitizePath(package_path.substr(FileLoader::kFileURLPrefixLength) +
112  library_path);
114  FileLoader::kPathSeparator + package_path +
115  FileLoader::kPathSeparator + library_path;
116 }
117 
118 Dart_Handle FileLoader::HandleLibraryTag(Dart_LibraryTag tag,
119  Dart_Handle library,
120  Dart_Handle url) {
121  TONIC_DCHECK(Dart_IsNull(library) || Dart_IsLibrary(library) ||
122  Dart_IsString(library));
123  TONIC_DCHECK(Dart_IsString(url));
124  if (tag == Dart_kCanonicalizeUrl)
125  return CanonicalizeURL(library, url);
126  if (tag == Dart_kKernelTag)
127  return Kernel(url);
128  if (tag == Dart_kImportTag)
129  return Import(url);
130  return Dart_NewApiError("Unknown library tag.");
131 }
132 
133 Dart_Handle FileLoader::CanonicalizeURL(Dart_Handle library, Dart_Handle url) {
134  std::string string = StdStringFromDart(url);
135  if (string.find(kDartScheme) == 0u)
136  return url;
137  if (string.find(kPackageScheme) == 0u)
138  return StdStringToDart(SanitizePath(string));
139  if (string.find(kFileScheme) == 0u)
140  return StdStringToDart(SanitizePath(CanonicalizeFileURL(string)));
141 
142  std::string library_url = StdStringFromDart(Dart_LibraryUrl(library));
143  std::string prefix = ExtractSchemePrefix(library_url);
144  std::string base_path = ExtractPath(library_url);
145  std::string simplified_path =
147  FileLoader::kPathSeparator + string);
148  return StdStringToDart(SanitizePath(prefix + simplified_path));
149 }
150 
151 std::string FileLoader::GetFilePathForURL(std::string url) {
152  if (url.find(kPackageScheme) == 0u)
153  return GetFilePathForPackageURL(std::move(url));
154  if (url.find(kFileScheme) == 0u)
155  return GetFilePathForFileURL(std::move(url));
156  return url;
157 }
158 
159 Dart_Handle FileLoader::FetchBytes(const std::string& url,
160  uint8_t*& buffer,
161  intptr_t& buffer_size) {
162  buffer = nullptr;
163  buffer_size = -1;
164 
165  std::string path = filesystem::SimplifyPath(GetFilePathForURL(url));
166  if (path.empty()) {
167  std::string error_message = "error: Unable to read '" + url + "'.";
168  return Dart_NewUnhandledExceptionError(
169  Dart_NewStringFromCString(error_message.c_str()));
170  }
171  std::string absolute_path = filesystem::GetAbsoluteFilePath(path);
172  auto result = filesystem::ReadFileToBytes(absolute_path);
173  if (result.first == nullptr) {
174  std::string error_message =
175  "error: Unable to read '" + absolute_path + "'.";
176  return Dart_NewUnhandledExceptionError(
177  Dart_NewStringFromCString(error_message.c_str()));
178  }
179  buffer = result.first;
180  buffer_size = result.second;
181  return Dart_True();
182 }
183 
184 Dart_Handle FileLoader::Import(Dart_Handle url) {
185  std::string url_string = StdStringFromDart(url);
186  uint8_t* buffer = nullptr;
187  intptr_t buffer_size = -1;
188  Dart_Handle result = FetchBytes(url_string, buffer, buffer_size);
189  if (Dart_IsError(result)) {
190  return result;
191  }
192  // The embedder must keep the buffer alive until isolate shutdown.
193  kernel_buffers_.push_back(buffer);
194  return Dart_LoadLibraryFromKernel(buffer, buffer_size);
195 }
196 
197 namespace {
198 void MallocFinalizer(void* isolate_callback_data, void* peer) {
199  free(peer);
200 }
201 } // namespace
202 
203 Dart_Handle FileLoader::Kernel(Dart_Handle url) {
204  std::string url_string = StdStringFromDart(url);
205  uint8_t* buffer = nullptr;
206  intptr_t buffer_size = -1;
207  Dart_Handle result = FetchBytes(url_string, buffer, buffer_size);
208  if (Dart_IsError(result)) {
209  return result;
210  }
211  result =
212  Dart_NewExternalTypedData(Dart_TypedData_kUint8, buffer, buffer_size);
213  Dart_NewFinalizableHandle(result, buffer, buffer_size, MallocFinalizer);
214  return result;
215 }
216 
217 // This is invoked upon a reload request.
218 void FileLoader::SetPackagesUrl(Dart_Handle url) {
219  if (url == Dart_Null()) {
220  // No packages url specified.
222  return;
223  }
224  const std::string& packages_url = StdStringFromDart(url);
225  LoadPackagesMap(packages_url);
226 }
227 
228 std::string FileLoader::GetFilePathForFileURL(std::string url) {
229  TONIC_DCHECK(url.find(FileLoader::kFileURLPrefix) == 0u);
230  return SanitizePath(url.substr(FileLoader::kFileURLPrefixLength));
231 }
232 
233 std::string FileLoader::GetFileURLForPath(const std::string& path) {
234  return std::string(FileLoader::kFileURLPrefix) + path;
235 }
236 
237 } // namespace tonic
DEF_SWITCHES_START snapshot asset path
Definition: switches.h:32
const std::string & packages() const
Definition: file_loader.h:27
void Log(const char *format,...)
Definition: log.cc:19
FileLoader(int dirfd=-1)
Definition: file_loader.cc:49
#define TONIC_DCHECK
Definition: macros.h:32
DEF_SWITCHES_START snapshot asset Path to the directory containing the four files specified by VmSnapshotInstructions and IsolateSnapshotInstructions vm snapshot The VM instructions snapshot that will be memory mapped as read and executable SnapshotAssetPath must be present isolate snapshot The isolate instructions snapshot that will be memory mapped as read and executable SnapshotAssetPath must be present icu symbol prefix
Definition: switches.h:59
FlMethodResponse GError ** error
Dart_Handle CanonicalizeURL(Dart_Handle library, Dart_Handle url)
Definition: file_loader.cc:133
std::pair< uint8_t *, intptr_t > ReadFileToBytes(const std::string &path)
Definition: file.cc:94
static const char kFileURLPrefix[]
Definition: file_loader.h:42
bool LoadPackagesMap(const std::string &packages)
Definition: file_loader.cc:79
std::string AbsolutePath(const std::string &path)
Definition: path_posix.cc:147
Dart_Handle Kernel(Dart_Handle url)
Definition: file_loader.cc:203
void SetPackagesUrl(Dart_Handle url)
Definition: file_loader.cc:218
static const std::string kPathSeparator
Definition: file_loader.h:44
std::string GetAbsoluteFilePath(const std::string &path)
Definition: path_posix.cc:176
std::string StdStringFromDart(Dart_Handle handle)
static const size_t kFileURLPrefixLength
Definition: file_loader.h:43
std::string GetDirectoryName(const std::string &path)
Definition: path_posix.cc:160
Dart_Handle HandleLibraryTag(Dart_LibraryTag tag, Dart_Handle library, Dart_Handle url)
Definition: file_loader.cc:118
Dart_Handle FetchBytes(const std::string &url, uint8_t *&buffer, intptr_t &buffer_size)
Definition: file_loader.cc:159
Dart_Handle Import(Dart_Handle url)
Definition: file_loader.cc:184
Dart_Handle StdStringToDart(const std::string &val)
std::string SimplifyPath(std::string path)
Definition: path_posix.cc:48