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
17extern "C" {
18#if !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM)
19extern const uint8_t kKernelServiceDill[];
20extern intptr_t kKernelServiceDillSize;
21extern const uint8_t kPlatformStrongDill[];
22extern intptr_t kPlatformStrongDillSize;
23#else
24const uint8_t* kKernelServiceDill = nullptr;
25intptr_t kKernelServiceDillSize = 0;
26const uint8_t* kPlatformStrongDill = nullptr;
27intptr_t kPlatformStrongDillSize = 0;
28#endif // !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM)
29}
30
31namespace dart {
32namespace 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))
41const uint8_t* kernel_service_dill = nullptr;
42const intptr_t kernel_service_dill_size = 0;
43#else
44const uint8_t* kernel_service_dill = kKernelServiceDill;
45const intptr_t kernel_service_dill_size = kKernelServiceDillSize;
46#endif
47
48#if defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM)
49const uint8_t* platform_strong_dill = nullptr;
50const intptr_t platform_strong_dill_size = 0;
51#else
52const uint8_t* platform_strong_dill = kPlatformStrongDill;
53const intptr_t platform_strong_dill_size = kPlatformStrongDillSize;
54#endif
55
56#if !defined(DART_PRECOMPILED_RUNTIME)
57DFE dfe;
58#endif
59
60const char kKernelServiceSnapshot[] = "kernel-service.dart.snapshot";
61const char kSnapshotsDirectory[] = "snapshots";
62
63DFE::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
71DFE::~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
82void 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
92void 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
121bool DFE::KernelServiceDillAvailable() const {
122 return kernel_service_dill != nullptr;
123}
124
125void 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
131void 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
137bool DFE::CanUseDartFrontend() const {
138 return (platform_strong_dill != nullptr) &&
139 (KernelServiceDillAvailable() || (frontend_filename() != nullptr));
140}
141
142PathSanitizer::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
175const 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
183Dart_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
196void 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
229void 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].
251static 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].
274static 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
288class 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
347class 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// ```
369static 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
421bool 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