1 | // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "bin/dfe.h" |
6 | |
7 | #include "bin/dartutils.h" |
8 | #include "bin/directory.h" |
9 | #include "bin/error_exit.h" |
10 | #include "bin/exe_utils.h" |
11 | #include "bin/file.h" |
12 | #include "bin/platform.h" |
13 | #include "bin/utils.h" |
14 | #include "include/dart_tools_api.h" |
15 | #include "platform/utils.h" |
16 | |
17 | extern "C" { |
18 | #if !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) |
19 | extern const uint8_t kKernelServiceDill[]; |
20 | extern intptr_t kKernelServiceDillSize; |
21 | extern const uint8_t kPlatformStrongDill[]; |
22 | extern intptr_t kPlatformStrongDillSize; |
23 | #else |
24 | const uint8_t* kKernelServiceDill = nullptr; |
25 | intptr_t kKernelServiceDillSize = 0; |
26 | const uint8_t* kPlatformStrongDill = nullptr; |
27 | intptr_t kPlatformStrongDillSize = 0; |
28 | #endif // !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) |
29 | } |
30 | |
31 | namespace dart { |
32 | namespace bin { |
33 | |
34 | // The run_vm_tests binary has the DART_PRECOMPILER set in order to allow unit |
35 | // tests to exercise JIT and AOT pipeline. |
36 | // |
37 | // Only on X64 do we have kernel-service.dart.snapshot available otherwise we |
38 | // need to fall back to the built-in one (if we have it). |
39 | #if defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) || \ |
40 | (defined(DART_PRECOMPILER) && defined(TARGET_ARCH_X64)) |
41 | const uint8_t* kernel_service_dill = nullptr; |
42 | const intptr_t kernel_service_dill_size = 0; |
43 | #else |
44 | const uint8_t* kernel_service_dill = kKernelServiceDill; |
45 | const intptr_t kernel_service_dill_size = kKernelServiceDillSize; |
46 | #endif |
47 | |
48 | #if defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) |
49 | const uint8_t* platform_strong_dill = nullptr; |
50 | const intptr_t platform_strong_dill_size = 0; |
51 | #else |
52 | const uint8_t* platform_strong_dill = kPlatformStrongDill; |
53 | const intptr_t platform_strong_dill_size = kPlatformStrongDillSize; |
54 | #endif |
55 | |
56 | #if !defined(DART_PRECOMPILED_RUNTIME) |
57 | DFE dfe; |
58 | #endif |
59 | |
60 | const char kKernelServiceSnapshot[] = "kernel-service.dart.snapshot" ; |
61 | const char kSnapshotsDirectory[] = "snapshots" ; |
62 | |
63 | DFE::DFE() |
64 | : use_dfe_(false), |
65 | use_incremental_compiler_(false), |
66 | frontend_filename_(nullptr), |
67 | application_kernel_buffer_(nullptr), |
68 | application_kernel_buffer_size_(0) { |
69 | } |
70 | |
71 | DFE::~DFE() { |
72 | if (frontend_filename_ != nullptr) { |
73 | free(frontend_filename_); |
74 | } |
75 | frontend_filename_ = nullptr; |
76 | |
77 | free(application_kernel_buffer_); |
78 | application_kernel_buffer_ = nullptr; |
79 | application_kernel_buffer_size_ = 0; |
80 | } |
81 | |
82 | void DFE::Init() { |
83 | if (platform_strong_dill == nullptr) { |
84 | return; |
85 | } |
86 | |
87 | InitKernelServiceAndPlatformDills(); |
88 | Dart_SetDartLibrarySourcesKernel(platform_strong_dill, |
89 | platform_strong_dill_size); |
90 | } |
91 | |
92 | void DFE::InitKernelServiceAndPlatformDills() { |
93 | if (frontend_filename_ != nullptr) { |
94 | return; |
95 | } |
96 | |
97 | // |dir_prefix| includes the last path seperator. |
98 | auto dir_prefix = EXEUtils::GetDirectoryPrefixFromExeName(); |
99 | |
100 | // Look for the frontend snapshot next to the executable. |
101 | frontend_filename_ = |
102 | Utils::SCreate("%s%s" , dir_prefix.get(), kKernelServiceSnapshot); |
103 | if (File::Exists(nullptr, frontend_filename_)) { |
104 | return; |
105 | } |
106 | free(frontend_filename_); |
107 | frontend_filename_ = nullptr; |
108 | |
109 | // If the frontend snapshot is not found next to the executable, then look for |
110 | // it in the "snapshots" directory. |
111 | frontend_filename_ = |
112 | Utils::SCreate("%s%s%s%s" , dir_prefix.get(), kSnapshotsDirectory, |
113 | File::PathSeparator(), kKernelServiceSnapshot); |
114 | if (File::Exists(nullptr, frontend_filename_)) { |
115 | return; |
116 | } |
117 | free(frontend_filename_); |
118 | frontend_filename_ = nullptr; |
119 | } |
120 | |
121 | bool DFE::KernelServiceDillAvailable() const { |
122 | return kernel_service_dill != nullptr; |
123 | } |
124 | |
125 | void DFE::LoadKernelService(const uint8_t** kernel_service_buffer, |
126 | intptr_t* kernel_service_buffer_size) { |
127 | *kernel_service_buffer = kernel_service_dill; |
128 | *kernel_service_buffer_size = kernel_service_dill_size; |
129 | } |
130 | |
131 | void DFE::LoadPlatform(const uint8_t** kernel_buffer, |
132 | intptr_t* kernel_buffer_size) { |
133 | *kernel_buffer = platform_strong_dill; |
134 | *kernel_buffer_size = platform_strong_dill_size; |
135 | } |
136 | |
137 | bool DFE::CanUseDartFrontend() const { |
138 | return (platform_strong_dill != nullptr) && |
139 | (KernelServiceDillAvailable() || (frontend_filename() != nullptr)); |
140 | } |
141 | |
142 | PathSanitizer::PathSanitizer(const char* path) { |
143 | #if defined(HOST_OS_WINDOWS) |
144 | // For Windows we need to massage the paths a bit according to |
145 | // http://blogs.msdn.com/b/ie/archive/2006/12/06/file-uris-in-windows.aspx |
146 | // |
147 | // Convert |
148 | // C:\one\two\three |
149 | // to |
150 | // /C:/one/two/three |
151 | // |
152 | // (see builtin.dart#_sanitizeWindowsPath) |
153 | if (path == nullptr) { |
154 | return; |
155 | } |
156 | intptr_t len = strlen(path); |
157 | char* uri = reinterpret_cast<char*>(new char[len + 1 + 1]); |
158 | if (uri == nullptr) { |
159 | OUT_OF_MEMORY(); |
160 | } |
161 | char* s = uri; |
162 | if (len > 2 && path[1] == ':') { |
163 | *s++ = '/'; |
164 | } |
165 | for (const char* p = path; *p != '\0'; ++p, ++s) { |
166 | *s = *p == '\\' ? '/' : *p; |
167 | } |
168 | *s = '\0'; |
169 | sanitized_uri_ = std::unique_ptr<char[]>(uri); |
170 | #else |
171 | sanitized_uri_ = path; |
172 | #endif // defined(HOST_OS_WINDOWS) |
173 | } |
174 | |
175 | const char* PathSanitizer::sanitized_uri() const { |
176 | #if defined(HOST_OS_WINDOWS) |
177 | return sanitized_uri_.get(); |
178 | #else |
179 | return sanitized_uri_; |
180 | #endif // defined(HOST_OS_WINDOWS) |
181 | } |
182 | |
183 | Dart_KernelCompilationResult DFE::CompileScript(const char* script_uri, |
184 | bool incremental, |
185 | const char* package_config) { |
186 | // TODO(aam): When Frontend is ready, VM should be passing vm_outline.dill |
187 | // instead of vm_platform.dill to Frontend for compilation. |
188 | PathSanitizer path_sanitizer(script_uri); |
189 | const char* sanitized_uri = path_sanitizer.sanitized_uri(); |
190 | |
191 | return Dart_CompileToKernel(sanitized_uri, platform_strong_dill, |
192 | platform_strong_dill_size, incremental, |
193 | package_config); |
194 | } |
195 | |
196 | void DFE::CompileAndReadScript(const char* script_uri, |
197 | uint8_t** kernel_buffer, |
198 | intptr_t* kernel_buffer_size, |
199 | char** error, |
200 | int* exit_code, |
201 | const char* package_config) { |
202 | Dart_KernelCompilationResult result = |
203 | CompileScript(script_uri, use_incremental_compiler(), package_config); |
204 | switch (result.status) { |
205 | case Dart_KernelCompilationStatus_Ok: |
206 | *kernel_buffer = result.kernel; |
207 | *kernel_buffer_size = result.kernel_size; |
208 | *error = nullptr; |
209 | *exit_code = 0; |
210 | break; |
211 | case Dart_KernelCompilationStatus_Error: |
212 | free(result.kernel); |
213 | *error = result.error; // Copy error message. |
214 | *exit_code = kCompilationErrorExitCode; |
215 | break; |
216 | case Dart_KernelCompilationStatus_Crash: |
217 | free(result.kernel); |
218 | *error = result.error; // Copy error message. |
219 | *exit_code = kDartFrontendErrorExitCode; |
220 | break; |
221 | case Dart_KernelCompilationStatus_Unknown: |
222 | free(result.kernel); |
223 | *error = result.error; // Copy error message. |
224 | *exit_code = kErrorExitCode; |
225 | break; |
226 | } |
227 | } |
228 | |
229 | void DFE::ReadScript(const char* script_uri, |
230 | uint8_t** kernel_buffer, |
231 | intptr_t* kernel_buffer_size) const { |
232 | int64_t start = Dart_TimelineGetMicros(); |
233 | if (!TryReadKernelFile(script_uri, kernel_buffer, kernel_buffer_size)) { |
234 | return; |
235 | } |
236 | if (!Dart_IsKernel(*kernel_buffer, *kernel_buffer_size)) { |
237 | free(*kernel_buffer); |
238 | *kernel_buffer = nullptr; |
239 | *kernel_buffer_size = -1; |
240 | } |
241 | int64_t end = Dart_TimelineGetMicros(); |
242 | Dart_TimelineEvent("DFE::ReadScript" , start, end, |
243 | Dart_Timeline_Event_Duration, 0, nullptr, nullptr); |
244 | } |
245 | |
246 | // Attempts to treat [buffer] as a in-memory kernel byte representation. |
247 | // If successful, returns [true] and places [buffer] into [kernel_ir], byte size |
248 | // into [kernel_ir_size]. |
249 | // If unsuccessful, returns [false], puts [nullptr] into [kernel_ir], -1 into |
250 | // [kernel_ir_size]. |
251 | static bool TryReadSimpleKernelBuffer(uint8_t* buffer, |
252 | uint8_t** p_kernel_ir, |
253 | intptr_t* p_kernel_ir_size) { |
254 | DartUtils::MagicNumber magic_number = |
255 | DartUtils::SniffForMagicNumber(buffer, *p_kernel_ir_size); |
256 | if (magic_number == DartUtils::kKernelMagicNumber) { |
257 | // Do not free buffer if this is a kernel file - kernel_file will be |
258 | // backed by the same memory as the buffer and caller will own it. |
259 | // Caller is responsible for freeing the buffer when this function |
260 | // returns true. |
261 | *p_kernel_ir = buffer; |
262 | return true; |
263 | } |
264 | free(buffer); |
265 | *p_kernel_ir = nullptr; |
266 | *p_kernel_ir_size = -1; |
267 | return false; |
268 | } |
269 | |
270 | /// Reads [script_uri] file, returns [true] if successful, [false] otherwise. |
271 | /// |
272 | /// If successful, newly allocated buffer with file contents is returned in |
273 | /// [buffer], file contents byte count - in [size]. |
274 | static bool TryReadFile(const char* script_uri, uint8_t** buffer, |
275 | intptr_t* size) { |
276 | void* script_file = DartUtils::OpenFileUri(script_uri, false); |
277 | if (script_file == nullptr) { |
278 | return false; |
279 | } |
280 | DartUtils::ReadFile(buffer, size, script_file); |
281 | DartUtils::CloseFile(script_file); |
282 | if (buffer == nullptr) { |
283 | return false; |
284 | } |
285 | return true; |
286 | } |
287 | |
288 | class KernelIRNode { |
289 | public: |
290 | KernelIRNode(uint8_t* kernel_ir, intptr_t kernel_size) |
291 | : kernel_ir_(kernel_ir), kernel_size_(kernel_size) {} |
292 | |
293 | ~KernelIRNode() { |
294 | free(kernel_ir_); |
295 | } |
296 | |
297 | static void Add(KernelIRNode** p_head, KernelIRNode** p_tail, |
298 | KernelIRNode* node) { |
299 | if (*p_head == nullptr) { |
300 | *p_head = node; |
301 | } else { |
302 | (*p_tail)->next_ = node; |
303 | } |
304 | *p_tail = node; |
305 | } |
306 | |
307 | static void Merge(KernelIRNode* head, uint8_t** p_bytes, |
308 | intptr_t* p_size) { |
309 | intptr_t size = 0; |
310 | for (KernelIRNode* node = head; node != nullptr; node = node->next_) { |
311 | size = size + node->kernel_size_; |
312 | } |
313 | |
314 | *p_bytes = reinterpret_cast<uint8_t*>(malloc(size)); |
315 | if (*p_bytes == nullptr) { |
316 | OUT_OF_MEMORY(); |
317 | } |
318 | uint8_t* p = *p_bytes; |
319 | KernelIRNode* node = head; |
320 | while (node != nullptr) { |
321 | memmove(p, node->kernel_ir_, node->kernel_size_); |
322 | p += node->kernel_size_; |
323 | KernelIRNode* next = node->next_; |
324 | node = next; |
325 | } |
326 | *p_size = size; |
327 | } |
328 | |
329 | static void Delete(KernelIRNode* head) { |
330 | KernelIRNode* node = head; |
331 | while (node != nullptr) { |
332 | KernelIRNode* next = node->next_; |
333 | delete (node); |
334 | node = next; |
335 | } |
336 | } |
337 | |
338 | private: |
339 | uint8_t* kernel_ir_; |
340 | intptr_t kernel_size_; |
341 | |
342 | KernelIRNode* next_ = nullptr; |
343 | |
344 | DISALLOW_COPY_AND_ASSIGN(KernelIRNode); |
345 | }; |
346 | |
347 | class StringPointer { |
348 | public: |
349 | explicit StringPointer(char* c_str) : c_str_(c_str) {} |
350 | ~StringPointer() { free(c_str_); } |
351 | |
352 | const char* c_str() { return c_str_; } |
353 | |
354 | private: |
355 | char* c_str_; |
356 | DISALLOW_COPY_AND_ASSIGN(StringPointer); |
357 | }; |
358 | |
359 | // Supports "kernel list" files as input. |
360 | // Those are text files that start with '#@dill' on new line, followed |
361 | // by absolute paths to kernel files or relative paths, that are relative |
362 | // to [script_uri] "kernel list" file. |
363 | // Below is an example of valid kernel list file: |
364 | // ``` |
365 | // #@dill |
366 | // /projects/mytest/build/bin/main.vm.dill |
367 | // /projects/mytest/build/packages/mytest/lib.vm.dill |
368 | // ``` |
369 | static bool TryReadKernelListBuffer(const char* script_uri, |
370 | uint8_t* buffer, |
371 | intptr_t buffer_size, |
372 | uint8_t** kernel_ir, |
373 | intptr_t* kernel_ir_size) { |
374 | const char* kernel_list_dirname = DartUtils::DirName(script_uri); |
375 | if (strcmp(kernel_list_dirname, script_uri) == 0) { |
376 | kernel_list_dirname = "" ; |
377 | } |
378 | KernelIRNode* kernel_ir_head = nullptr; |
379 | KernelIRNode* kernel_ir_tail = nullptr; |
380 | // Add all kernels to the linked list |
381 | char* filename = |
382 | reinterpret_cast<char*>(buffer + kernel_list_magic_number.length); |
383 | intptr_t filename_size = buffer_size - kernel_list_magic_number.length; |
384 | char* tail = reinterpret_cast<char*>(memchr(filename, '\n', filename_size)); |
385 | while (tail != nullptr) { |
386 | *tail = '\0'; |
387 | intptr_t this_kernel_size; |
388 | uint8_t* this_buffer; |
389 | |
390 | StringPointer resolved_filename( |
391 | File::IsAbsolutePath(filename) |
392 | ? Utils::StrDup(filename) |
393 | : Utils::SCreate("%s%s" , kernel_list_dirname, filename)); |
394 | if (!TryReadFile(resolved_filename.c_str(), &this_buffer, |
395 | &this_kernel_size)) { |
396 | return false; |
397 | } |
398 | |
399 | uint8_t* this_kernel_ir; |
400 | if (!TryReadSimpleKernelBuffer(this_buffer, &this_kernel_ir, |
401 | &this_kernel_size)) { |
402 | // Abandon read if any of the files in the list are invalid. |
403 | KernelIRNode::Delete(kernel_ir_head); |
404 | *kernel_ir = nullptr; |
405 | *kernel_ir_size = -1; |
406 | return false; |
407 | } |
408 | KernelIRNode::Add(&kernel_ir_head, &kernel_ir_tail, |
409 | new KernelIRNode(this_kernel_ir, this_kernel_size)); |
410 | filename_size -= tail + 1 - filename; |
411 | filename = tail + 1; |
412 | tail = reinterpret_cast<char*>(memchr(filename, '\n', filename_size)); |
413 | } |
414 | free(buffer); |
415 | |
416 | KernelIRNode::Merge(kernel_ir_head, kernel_ir, kernel_ir_size); |
417 | KernelIRNode::Delete(kernel_ir_head); |
418 | return true; |
419 | } |
420 | |
421 | bool DFE::TryReadKernelFile(const char* script_uri, |
422 | uint8_t** kernel_ir, |
423 | intptr_t* kernel_ir_size) { |
424 | *kernel_ir = nullptr; |
425 | *kernel_ir_size = -1; |
426 | |
427 | uint8_t* buffer; |
428 | if (!TryReadFile(script_uri, &buffer, kernel_ir_size)) { |
429 | return false; |
430 | } |
431 | |
432 | DartUtils::MagicNumber magic_number = |
433 | DartUtils::SniffForMagicNumber(buffer, *kernel_ir_size); |
434 | if (magic_number == DartUtils::kKernelListMagicNumber) { |
435 | return TryReadKernelListBuffer(script_uri, buffer, *kernel_ir_size, |
436 | kernel_ir, kernel_ir_size); |
437 | } |
438 | return TryReadSimpleKernelBuffer(buffer, kernel_ir, kernel_ir_size); |
439 | } |
440 | |
441 | } // namespace bin |
442 | } // namespace dart |
443 | |