| // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| #include "bin/dfe.h" | |
| #include "bin/dartutils.h" | |
| #include "bin/directory.h" | |
| #include "bin/error_exit.h" | |
| #include "bin/exe_utils.h" | |
| #include "bin/file.h" | |
| #include "bin/platform.h" | |
| #include "bin/snapshot_utils.h" | |
| #include "bin/utils.h" | |
| #include "include/dart_tools_api.h" | |
| #include "platform/utils.h" | |
| extern "C" { | |
| #if !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) | |
| extern const uint8_t kKernelServiceDill[]; | |
| extern intptr_t kKernelServiceDillSize; | |
| extern const uint8_t kPlatformDill[]; | |
| extern intptr_t kPlatformDillSize; | |
| #else | |
| const uint8_t* kKernelServiceDill = nullptr; | |
| intptr_t kKernelServiceDillSize = 0; | |
| const uint8_t* kPlatformDill = nullptr; | |
| intptr_t kPlatformDillSize = 0; | |
| #endif // !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) | |
| } | |
| namespace dart { | |
| namespace bin { | |
| // The run_vm_tests binary has the DART_PRECOMPILER set in order to allow unit | |
| // tests to exercise JIT and AOT pipeline. | |
| // | |
| // Only on X64 do we have kernel-service.dart.snapshot available otherwise we | |
| // need to fall back to the built-in one (if we have it). | |
| #if defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) || \ | |
| (defined(DART_PRECOMPILER) && defined(TARGET_ARCH_X64)) | |
| const uint8_t* kernel_service_dill = nullptr; | |
| const intptr_t kernel_service_dill_size = 0; | |
| #else | |
| const uint8_t* kernel_service_dill = kKernelServiceDill; | |
| const intptr_t kernel_service_dill_size = kKernelServiceDillSize; | |
| #endif | |
| #if defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) | |
| const uint8_t* platform_dill = nullptr; | |
| const intptr_t platform_dill_size = 0; | |
| #else | |
| const uint8_t* platform_dill = kPlatformDill; | |
| const intptr_t platform_dill_size = kPlatformDillSize; | |
| #endif | |
| #if !defined(DART_PRECOMPILED_RUNTIME) | |
| DFE dfe; | |
| #endif | |
| const char kKernelServiceSnapshot[] = "kernel-service.dart.snapshot"; | |
| const char kSnapshotsDirectory[] = "snapshots"; | |
| DFE::DFE() | |
| : use_dfe_(false), | |
| use_incremental_compiler_(false), | |
| frontend_filename_(nullptr), | |
| application_kernel_buffer_(nullptr), | |
| application_kernel_buffer_size_(0) {} | |
| DFE::~DFE() { | |
| if (frontend_filename_ != nullptr) { | |
| free(frontend_filename_); | |
| } | |
| frontend_filename_ = nullptr; | |
| free(application_kernel_buffer_); | |
| application_kernel_buffer_ = nullptr; | |
| application_kernel_buffer_size_ = 0; | |
| } | |
| void DFE::Init() { | |
| if (platform_dill == nullptr) { | |
| return; | |
| } | |
| InitKernelServiceAndPlatformDills(); | |
| Dart_SetDartLibrarySourcesKernel(platform_dill, platform_dill_size); | |
| } | |
| void DFE::InitKernelServiceAndPlatformDills() { | |
| if (frontend_filename_ != nullptr) { | |
| return; | |
| } | |
| // |dir_prefix| includes the last path separator. | |
| auto dir_prefix = EXEUtils::GetDirectoryPrefixFromResolvedExeName(); | |
| // Look for the frontend snapshot next to the executable. | |
| frontend_filename_ = | |
| Utils::SCreate("%s%s", dir_prefix.get(), kKernelServiceSnapshot); | |
| if (File::Exists(nullptr, frontend_filename_)) { | |
| return; | |
| } | |
| free(frontend_filename_); | |
| frontend_filename_ = nullptr; | |
| // If the frontend snapshot is not found next to the executable, then look for | |
| // it in the "snapshots" directory. | |
| frontend_filename_ = | |
| Utils::SCreate("%s%s%s%s", dir_prefix.get(), kSnapshotsDirectory, | |
| File::PathSeparator(), kKernelServiceSnapshot); | |
| if (File::Exists(nullptr, frontend_filename_)) { | |
| return; | |
| } | |
| free(frontend_filename_); | |
| frontend_filename_ = nullptr; | |
| } | |
| bool DFE::KernelServiceDillAvailable() const { | |
| return kernel_service_dill != nullptr; | |
| } | |
| void DFE::LoadKernelService(const uint8_t** kernel_service_buffer, | |
| intptr_t* kernel_service_buffer_size) { | |
| *kernel_service_buffer = kernel_service_dill; | |
| *kernel_service_buffer_size = kernel_service_dill_size; | |
| } | |
| void DFE::LoadPlatform(const uint8_t** kernel_buffer, | |
| intptr_t* kernel_buffer_size) { | |
| *kernel_buffer = platform_dill; | |
| *kernel_buffer_size = platform_dill_size; | |
| } | |
| bool DFE::CanUseDartFrontend() const { | |
| return (platform_dill != nullptr) && | |
| (KernelServiceDillAvailable() || (frontend_filename() != nullptr)); | |
| } | |
| PathSanitizer::PathSanitizer(const char* path) { | |
| #if defined(DART_HOST_OS_WINDOWS) | |
| // For Windows we need to massage the paths a bit according to | |
| // http://blogs.msdn.com/b/ie/archive/2006/12/06/file-uris-in-windows.aspx | |
| // | |
| // Convert | |
| // C:\one\two\three | |
| // to | |
| // /C:/one/two/three | |
| // | |
| // (see builtin.dart#_sanitizeWindowsPath) | |
| if (path == nullptr) { | |
| return; | |
| } | |
| intptr_t len = strlen(path); | |
| char* uri = reinterpret_cast<char*>(new char[len + 1 + 1]); | |
| if (uri == nullptr) { | |
| OUT_OF_MEMORY(); | |
| } | |
| char* s = uri; | |
| if (len > 2 && path[1] == ':') { | |
| *s++ = '/'; | |
| } | |
| for (const char* p = path; *p != '\0'; ++p, ++s) { | |
| *s = *p == '\\' ? '/' : *p; | |
| } | |
| *s = '\0'; | |
| sanitized_uri_ = std::unique_ptr<char[]>(uri); | |
| #else | |
| sanitized_uri_ = path; | |
| #endif // defined(DART_HOST_OS_WINDOWS) | |
| } | |
| const char* PathSanitizer::sanitized_uri() const { | |
| #if defined(DART_HOST_OS_WINDOWS) | |
| return sanitized_uri_.get(); | |
| #else | |
| return sanitized_uri_; | |
| #endif // defined(DART_HOST_OS_WINDOWS) | |
| } | |
| Dart_KernelCompilationResult DFE::CompileScript(const char* script_uri, | |
| bool incremental, | |
| const char* package_config, | |
| bool for_snapshot, | |
| bool embed_sources) { | |
| // TODO(aam): When Frontend is ready, VM should be passing vm_outline.dill | |
| // instead of vm_platform.dill to Frontend for compilation. | |
| PathSanitizer path_sanitizer(script_uri); | |
| const char* sanitized_uri = path_sanitizer.sanitized_uri(); | |
| return Dart_CompileToKernel(sanitized_uri, platform_dill, platform_dill_size, | |
| incremental, for_snapshot, embed_sources, | |
| package_config, verbosity()); | |
| } | |
| void DFE::CompileAndReadScript(const char* script_uri, | |
| uint8_t** kernel_buffer, | |
| intptr_t* kernel_buffer_size, | |
| char** error, | |
| int* exit_code, | |
| const char* package_config, | |
| bool for_snapshot, | |
| bool embed_sources) { | |
| Dart_KernelCompilationResult result = | |
| CompileScript(script_uri, use_incremental_compiler(), package_config, | |
| for_snapshot, embed_sources); | |
| switch (result.status) { | |
| case Dart_KernelCompilationStatus_Ok: | |
| *kernel_buffer = result.kernel; | |
| *kernel_buffer_size = result.kernel_size; | |
| *error = nullptr; | |
| *exit_code = 0; | |
| break; | |
| case Dart_KernelCompilationStatus_Error: | |
| *error = result.error; // Copy error message. | |
| *exit_code = kCompilationErrorExitCode; | |
| break; | |
| case Dart_KernelCompilationStatus_Crash: | |
| *error = result.error; // Copy error message. | |
| *exit_code = kDartFrontendErrorExitCode; | |
| break; | |
| case Dart_KernelCompilationStatus_Unknown: | |
| case Dart_KernelCompilationStatus_MsgFailed: | |
| *error = result.error; // Copy error message. | |
| *exit_code = kErrorExitCode; | |
| break; | |
| } | |
| } | |
| void DFE::ReadScript(const char* script_uri, | |
| const AppSnapshot* app_snapshot, | |
| uint8_t** kernel_buffer, | |
| intptr_t* kernel_buffer_size, | |
| bool decode_uri) const { | |
| int64_t start = Dart_TimelineGetMicros(); | |
| if (!TryReadKernelFile(script_uri, app_snapshot, kernel_buffer, | |
| kernel_buffer_size, decode_uri)) { | |
| return; | |
| } | |
| if (!Dart_IsKernel(*kernel_buffer, *kernel_buffer_size) && | |
| !Dart_IsBytecode(*kernel_buffer, *kernel_buffer_size)) { | |
| free(*kernel_buffer); | |
| *kernel_buffer = nullptr; | |
| *kernel_buffer_size = -1; | |
| } | |
| int64_t end = Dart_TimelineGetMicros(); | |
| Dart_RecordTimelineEvent("DFE::ReadScript", start, end, /*flow_id_count=*/0, | |
| nullptr, Dart_Timeline_Event_Duration, | |
| /*argument_count=*/0, nullptr, nullptr); | |
| } | |
| // Attempts to treat [buffer] as a in-memory kernel byte representation. | |
| // If successful, returns [true] and places [buffer] into [kernel_ir], byte size | |
| // into [kernel_ir_size]. | |
| // If unsuccessful, returns [false], puts [nullptr] into [kernel_ir], -1 into | |
| // [kernel_ir_size]. | |
| static bool TryReadSimpleKernelBuffer(uint8_t* buffer, | |
| uint8_t** p_kernel_ir, | |
| intptr_t* p_kernel_ir_size) { | |
| DartUtils::MagicNumber magic_number = | |
| DartUtils::SniffForMagicNumber(buffer, *p_kernel_ir_size); | |
| if ((magic_number == DartUtils::kKernelMagicNumber) || | |
| (magic_number == DartUtils::kBytecodeMagicNumber)) { | |
| // Do not free buffer if this is a kernel file - kernel_file will be | |
| // backed by the same memory as the buffer and caller will own it. | |
| // Caller is responsible for freeing the buffer when this function | |
| // returns true. | |
| *p_kernel_ir = buffer; | |
| return true; | |
| } | |
| free(buffer); | |
| *p_kernel_ir = nullptr; | |
| *p_kernel_ir_size = -1; | |
| return false; | |
| } | |
| /// Reads [script_uri] file, returns [true] if successful, [false] otherwise. | |
| /// | |
| /// If successful, newly allocated buffer with file contents is returned in | |
| /// [buffer], file contents byte count - in [size]. | |
| static bool TryReadFile(const char* script_uri, | |
| uint8_t** buffer, | |
| intptr_t* size, | |
| bool decode_uri = true) { | |
| void* script_file = decode_uri ? DartUtils::OpenFileUri(script_uri, false) | |
| : DartUtils::OpenFile(script_uri, false); | |
| if (script_file == nullptr) { | |
| return false; | |
| } | |
| DartUtils::ReadFile(buffer, size, script_file); | |
| DartUtils::CloseFile(script_file); | |
| return *buffer != nullptr; | |
| } | |
| class KernelIRNode { | |
| public: | |
| KernelIRNode(uint8_t* kernel_ir, intptr_t kernel_size) | |
| : kernel_ir_(kernel_ir), kernel_size_(kernel_size) {} | |
| ~KernelIRNode() { free(kernel_ir_); } | |
| static void Add(KernelIRNode** p_head, | |
| KernelIRNode** p_tail, | |
| KernelIRNode* node) { | |
| if (*p_head == nullptr) { | |
| *p_head = node; | |
| } else { | |
| (*p_tail)->next_ = node; | |
| } | |
| *p_tail = node; | |
| } | |
| static void Merge(KernelIRNode* head, uint8_t** p_bytes, intptr_t* p_size) { | |
| intptr_t size = 0; | |
| for (KernelIRNode* node = head; node != nullptr; node = node->next_) { | |
| size = size + node->kernel_size_; | |
| } | |
| *p_bytes = reinterpret_cast<uint8_t*>(malloc(size)); | |
| uint8_t* p = *p_bytes; | |
| KernelIRNode* node = head; | |
| while (node != nullptr) { | |
| memmove(p, node->kernel_ir_, node->kernel_size_); | |
| p += node->kernel_size_; | |
| KernelIRNode* next = node->next_; | |
| node = next; | |
| } | |
| *p_size = size; | |
| } | |
| static void Delete(KernelIRNode* head) { | |
| KernelIRNode* node = head; | |
| while (node != nullptr) { | |
| KernelIRNode* next = node->next_; | |
| delete (node); | |
| node = next; | |
| } | |
| } | |
| private: | |
| uint8_t* kernel_ir_; | |
| intptr_t kernel_size_; | |
| KernelIRNode* next_ = nullptr; | |
| DISALLOW_COPY_AND_ASSIGN(KernelIRNode); | |
| }; | |
| class StringPointer { | |
| public: | |
| explicit StringPointer(char* c_str) : c_str_(c_str) {} | |
| ~StringPointer() { free(c_str_); } | |
| const char* c_str() { return c_str_; } | |
| private: | |
| char* c_str_; | |
| DISALLOW_COPY_AND_ASSIGN(StringPointer); | |
| }; | |
| // Supports "kernel list" files as input. | |
| // Those are text files that start with '#@dill' on new line, followed | |
| // by absolute paths to kernel files or relative paths, that are relative | |
| // to [script_uri] "kernel list" file. | |
| // Below is an example of valid kernel list file: | |
| // ``` | |
| // #@dill | |
| // /projects/mytest/build/bin/main.vm.dill | |
| // /projects/mytest/build/packages/mytest/lib.vm.dill | |
| // ``` | |
| static bool TryReadKernelListBuffer(const char* script_uri, | |
| uint8_t* buffer, | |
| intptr_t buffer_size, | |
| uint8_t** kernel_ir, | |
| intptr_t* kernel_ir_size) { | |
| const char* kernel_list_dirname = DartUtils::DirName(script_uri); | |
| if (strcmp(kernel_list_dirname, script_uri) == 0) { | |
| kernel_list_dirname = ""; | |
| } | |
| KernelIRNode* kernel_ir_head = nullptr; | |
| KernelIRNode* kernel_ir_tail = nullptr; | |
| // Add all kernels to the linked list | |
| char* filename = | |
| reinterpret_cast<char*>(buffer + kernel_list_magic_number.length); | |
| intptr_t filename_size = buffer_size - kernel_list_magic_number.length; | |
| char* tail = reinterpret_cast<char*>(memchr(filename, '\n', filename_size)); | |
| while (tail != nullptr) { | |
| *tail = '\0'; | |
| intptr_t this_kernel_size; | |
| uint8_t* this_buffer; | |
| StringPointer resolved_filename( | |
| File::IsAbsolutePath(filename) | |
| ? Utils::StrDup(filename) | |
| : Utils::SCreate("%s%s", kernel_list_dirname, filename)); | |
| if (!TryReadFile(resolved_filename.c_str(), &this_buffer, | |
| &this_kernel_size)) { | |
| return false; | |
| } | |
| uint8_t* this_kernel_ir; | |
| if (!TryReadSimpleKernelBuffer(this_buffer, &this_kernel_ir, | |
| &this_kernel_size)) { | |
| // Abandon read if any of the files in the list are invalid. | |
| KernelIRNode::Delete(kernel_ir_head); | |
| *kernel_ir = nullptr; | |
| *kernel_ir_size = -1; | |
| return false; | |
| } | |
| KernelIRNode::Add(&kernel_ir_head, &kernel_ir_tail, | |
| new KernelIRNode(this_kernel_ir, this_kernel_size)); | |
| filename_size -= tail + 1 - filename; | |
| filename = tail + 1; | |
| tail = reinterpret_cast<char*>(memchr(filename, '\n', filename_size)); | |
| } | |
| free(buffer); | |
| KernelIRNode::Merge(kernel_ir_head, kernel_ir, kernel_ir_size); | |
| KernelIRNode::Delete(kernel_ir_head); | |
| return true; | |
| } | |
| bool DFE::TryReadKernelFile(const char* script_uri, | |
| const AppSnapshot* app_snapshot, | |
| uint8_t** kernel_ir, | |
| intptr_t* kernel_ir_size, | |
| bool decode_uri) const { | |
| *kernel_ir = nullptr; | |
| *kernel_ir_size = -1; | |
| if (app_snapshot == nullptr || app_snapshot->IsKernel() || | |
| app_snapshot->IsKernelList() || app_snapshot->IsBytecode()) { | |
| uint8_t* buffer; | |
| if (!TryReadFile(script_uri, &buffer, kernel_ir_size, decode_uri)) { | |
| return false; | |
| } | |
| auto magic_number = DartUtils::kUnknownMagicNumber; | |
| if (app_snapshot == nullptr) { | |
| magic_number = DartUtils::SniffForMagicNumber(buffer, *kernel_ir_size); | |
| } else if (app_snapshot->IsKernel()) { | |
| magic_number = DartUtils::kKernelMagicNumber; | |
| ASSERT(DartUtils::SniffForMagicNumber(buffer, *kernel_ir_size) == | |
| DartUtils::kKernelMagicNumber); | |
| } else if (app_snapshot->IsBytecode()) { | |
| magic_number = DartUtils::kBytecodeMagicNumber; | |
| ASSERT(DartUtils::SniffForMagicNumber(buffer, *kernel_ir_size) == | |
| DartUtils::kBytecodeMagicNumber); | |
| } else { | |
| magic_number = DartUtils::kKernelListMagicNumber; | |
| ASSERT(DartUtils::SniffForMagicNumber(buffer, *kernel_ir_size) == | |
| DartUtils::kKernelListMagicNumber); | |
| } | |
| if (magic_number == DartUtils::kKernelListMagicNumber) { | |
| return TryReadKernelListBuffer(script_uri, buffer, *kernel_ir_size, | |
| kernel_ir, kernel_ir_size); | |
| } | |
| return TryReadSimpleKernelBuffer(buffer, kernel_ir, kernel_ir_size); | |
| } | |
| return false; | |
| } | |
| } // namespace bin | |
| } // namespace dart |