| // Copyright (c) 2016, 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/loader.h" |
| |
| #include "bin/builtin.h" |
| #include "bin/dartutils.h" |
| #include "bin/dfe.h" |
| #include "bin/error_exit.h" |
| #include "bin/extensions.h" |
| #include "bin/file.h" |
| #include "bin/gzip.h" |
| #include "bin/lockers.h" |
| #include "bin/utils.h" |
| #include "include/dart_tools_api.h" |
| #include "platform/growable_array.h" |
| |
| namespace dart { |
| namespace bin { |
| |
| // Development flag. |
| static bool trace_loader = false; |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| extern DFE dfe; |
| #endif |
| |
| // Keep in sync with loader.dart. |
| static const intptr_t _Dart_kImportExtension = 9; |
| static const intptr_t _Dart_kResolveAsFilePath = 10; |
| |
| Loader::Loader(IsolateData* isolate_data) |
| : port_(ILLEGAL_PORT), |
| isolate_data_(isolate_data), |
| error_(Dart_Null()), |
| monitor_(NULL), |
| pending_operations_(0), |
| results_(NULL), |
| results_length_(0), |
| results_capacity_(0), |
| payload_(NULL), |
| payload_length_(0) { |
| monitor_ = new Monitor(); |
| ASSERT(isolate_data_ != NULL); |
| port_ = Dart_NewNativePort("Loader", Loader::NativeMessageHandler, false); |
| isolate_data_->set_loader(this); |
| AddLoader(port_, isolate_data_); |
| } |
| |
| Loader::~Loader() { |
| ASSERT(port_ != ILLEGAL_PORT); |
| // Enter the monitor while we close the Dart port. After the Dart port is |
| // closed, no more results can be queued. |
| monitor_->Enter(); |
| Dart_CloseNativePort(port_); |
| monitor_->Exit(); |
| RemoveLoader(port_); |
| port_ = ILLEGAL_PORT; |
| isolate_data_->set_loader(NULL); |
| isolate_data_ = NULL; |
| delete monitor_; |
| monitor_ = NULL; |
| for (intptr_t i = 0; i < results_length_; i++) { |
| results_[i].Cleanup(); |
| } |
| free(results_); |
| results_ = NULL; |
| payload_ = NULL; |
| payload_length_ = 0; |
| } |
| |
| // Copy the contents of |message| into an |IOResult|. |
| void Loader::IOResult::Setup(Dart_CObject* message) { |
| ASSERT(message->type == Dart_CObject_kArray); |
| ASSERT(message->value.as_array.length == 5); |
| Dart_CObject* tag_message = message->value.as_array.values[0]; |
| ASSERT(tag_message != NULL); |
| Dart_CObject* uri_message = message->value.as_array.values[1]; |
| ASSERT(uri_message != NULL); |
| Dart_CObject* resolved_uri_message = message->value.as_array.values[2]; |
| ASSERT(resolved_uri_message != NULL); |
| Dart_CObject* library_uri_message = message->value.as_array.values[3]; |
| ASSERT(library_uri_message != NULL); |
| Dart_CObject* payload_message = message->value.as_array.values[4]; |
| ASSERT(payload_message != NULL); |
| |
| // Grab the tag. |
| ASSERT(tag_message->type == Dart_CObject_kInt32); |
| tag = tag_message->value.as_int32; |
| |
| // Grab the uri id. |
| ASSERT(uri_message->type == Dart_CObject_kString); |
| uri = strdup(uri_message->value.as_string); |
| |
| // Grab the resolved uri. |
| ASSERT(resolved_uri_message->type == Dart_CObject_kString); |
| resolved_uri = strdup(resolved_uri_message->value.as_string); |
| |
| // Grab the library uri if one is present. |
| if (library_uri_message->type != Dart_CObject_kNull) { |
| ASSERT(library_uri_message->type == Dart_CObject_kString); |
| library_uri = strdup(library_uri_message->value.as_string); |
| } else { |
| library_uri = NULL; |
| } |
| |
| // Grab the payload. |
| if (payload_message->type == Dart_CObject_kString) { |
| // Payload is an error message. |
| payload_length = strlen(payload_message->value.as_string); |
| payload = |
| reinterpret_cast<uint8_t*>(strdup(payload_message->value.as_string)); |
| } else { |
| // Payload is the contents of a file. |
| ASSERT(payload_message->type == Dart_CObject_kTypedData); |
| ASSERT(payload_message->value.as_typed_data.type == Dart_TypedData_kUint8); |
| payload_length = payload_message->value.as_typed_data.length; |
| payload = reinterpret_cast<uint8_t*>(malloc(payload_length)); |
| memmove(payload, payload_message->value.as_typed_data.values, |
| payload_length); |
| } |
| } |
| |
| void Loader::IOResult::Cleanup() { |
| free(uri); |
| free(resolved_uri); |
| free(library_uri); |
| free(payload); |
| } |
| |
| // Send the Loader Initialization message to the service isolate. This |
| // message is sent the first time a loader is constructed for an isolate and |
| // seeds the service isolate with some initial state about this isolate. |
| void Loader::Init(const char* package_root, |
| const char* packages_file, |
| const char* working_directory, |
| const char* root_script_uri) { |
| // This port delivers loading messages to the service isolate. |
| Dart_Port loader_port = Builtin::LoadPort(); |
| ASSERT(loader_port != ILLEGAL_PORT); |
| |
| // Keep in sync with loader.dart. |
| const intptr_t _Dart_kInitLoader = 4; |
| |
| Dart_Handle request = Dart_NewList(9); |
| Dart_ListSetAt(request, 0, trace_loader ? Dart_True() : Dart_False()); |
| Dart_ListSetAt(request, 1, Dart_NewInteger(Dart_GetMainPortId())); |
| Dart_ListSetAt(request, 2, Dart_NewInteger(_Dart_kInitLoader)); |
| Dart_ListSetAt(request, 3, Dart_NewSendPort(port_)); |
| Dart_ListSetAt(request, 4, |
| (package_root == NULL) |
| ? Dart_Null() |
| : Dart_NewStringFromCString(package_root)); |
| Dart_ListSetAt(request, 5, |
| (packages_file == NULL) |
| ? Dart_Null() |
| : Dart_NewStringFromCString(packages_file)); |
| Dart_ListSetAt(request, 6, Dart_NewStringFromCString(working_directory)); |
| Dart_ListSetAt(request, 7, |
| (root_script_uri == NULL) |
| ? Dart_Null() |
| : Dart_NewStringFromCString(root_script_uri)); |
| Dart_ListSetAt(request, 8, Dart_NewBoolean(Dart_IsReloading())); |
| |
| bool success = Dart_Post(loader_port, request); |
| ASSERT(success); |
| } |
| |
| void Loader::SendImportExtensionRequest(Dart_Handle url, |
| Dart_Handle library_url) { |
| // This port delivers loading messages to the service isolate. |
| Dart_Port loader_port = Builtin::LoadPort(); |
| ASSERT(loader_port != ILLEGAL_PORT); |
| |
| Dart_Handle request = Dart_NewList(6); |
| Dart_ListSetAt(request, 0, trace_loader ? Dart_True() : Dart_False()); |
| Dart_ListSetAt(request, 1, Dart_NewInteger(Dart_GetMainPortId())); |
| Dart_ListSetAt(request, 2, Dart_NewInteger(_Dart_kImportExtension)); |
| Dart_ListSetAt(request, 3, Dart_NewSendPort(port_)); |
| |
| Dart_ListSetAt(request, 4, url); |
| Dart_ListSetAt(request, 5, library_url); |
| |
| if (Dart_Post(loader_port, request)) { |
| MonitorLocker ml(monitor_); |
| pending_operations_++; |
| } |
| } |
| |
| // Forward a request from the tag handler to the service isolate. |
| void Loader::SendRequest(intptr_t tag, |
| Dart_Handle url, |
| Dart_Handle library_url) { |
| // This port delivers loading messages to the service isolate. |
| Dart_Port loader_port = Builtin::LoadPort(); |
| ASSERT(loader_port != ILLEGAL_PORT); |
| |
| Dart_Handle request = Dart_NewList(6); |
| Dart_ListSetAt(request, 0, trace_loader ? Dart_True() : Dart_False()); |
| Dart_ListSetAt(request, 1, Dart_NewInteger(Dart_GetMainPortId())); |
| Dart_ListSetAt(request, 2, Dart_NewInteger(tag)); |
| Dart_ListSetAt(request, 3, Dart_NewSendPort(port_)); |
| |
| Dart_ListSetAt(request, 4, url); |
| Dart_ListSetAt(request, 5, library_url); |
| |
| if (Dart_Post(loader_port, request)) { |
| MonitorLocker ml(monitor_); |
| pending_operations_++; |
| } |
| } |
| |
| void Loader::QueueMessage(Dart_CObject* message) { |
| MonitorLocker ml(monitor_); |
| if (results_length_ == results_capacity_) { |
| // Grow to an initial capacity or double in size. |
| results_capacity_ = (results_capacity_ == 0) ? 4 : results_capacity_ * 2; |
| results_ = reinterpret_cast<IOResult*>( |
| realloc(results_, sizeof(IOResult) * results_capacity_)); |
| ASSERT(results_ != NULL); |
| } |
| ASSERT(results_ != NULL); |
| ASSERT(results_length_ < results_capacity_); |
| results_[results_length_].Setup(message); |
| results_length_++; |
| ml.Notify(); |
| } |
| |
| void Loader::BlockUntilComplete(ProcessResult process_result) { |
| MonitorLocker ml(monitor_); |
| |
| while (true) { |
| // If |ProcessQueueLocked| returns false, we've hit an error and should |
| // stop loading. |
| if (!ProcessQueueLocked(process_result)) { |
| break; |
| } |
| |
| // When |pending_operations_| hits 0, we are done loading. |
| if (pending_operations_ == 0) { |
| break; |
| } |
| |
| // Wait to be notified about new I/O results. |
| ml.Wait(); |
| } |
| } |
| |
| static bool LibraryHandleError(Dart_Handle library, Dart_Handle error) { |
| if (!Dart_IsNull(library) && !Dart_IsError(library)) { |
| ASSERT(Dart_IsLibrary(library)); |
| Dart_Handle res = Dart_LibraryHandleError(library, error); |
| if (Dart_IsNull(res)) { |
| // Error was handled by library. |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| static bool PathContainsSeparator(const char* path) { |
| return (strchr(path, '/') != NULL) || |
| ((strncmp(File::PathSeparator(), "/", 1) != 0) && |
| (strstr(path, File::PathSeparator()) != NULL)); |
| } |
| |
| void Loader::AddDependencyLocked(Loader* loader, const char* resolved_uri) { |
| MallocGrowableArray<char*>* dependencies = |
| loader->isolate_data_->dependencies(); |
| if (dependencies == NULL) { |
| return; |
| } |
| dependencies->Add(strdup(resolved_uri)); |
| } |
| |
| void Loader::ResolveDependenciesAsFilePaths() { |
| IsolateData* isolate_data = |
| reinterpret_cast<IsolateData*>(Dart_CurrentIsolateData()); |
| ASSERT(isolate_data != NULL); |
| MallocGrowableArray<char*>* dependencies = isolate_data->dependencies(); |
| if (dependencies == NULL) { |
| return; |
| } |
| |
| for (intptr_t i = 0; i < dependencies->length(); i++) { |
| char* resolved_uri = (*dependencies)[i]; |
| |
| uint8_t* file_path = NULL; |
| intptr_t file_path_length = -1; |
| Dart_Handle uri = Dart_NewStringFromCString(resolved_uri); |
| ASSERT(!Dart_IsError(uri)); |
| Dart_Handle result = |
| Loader::ResolveAsFilePath(uri, &file_path, &file_path_length); |
| if (Dart_IsError(result)) { |
| Log::Print("Error resolving dependency: %s\n", Dart_GetError(result)); |
| return; |
| } |
| |
| // Convert buffer buffer to NUL-terminated string. |
| (*dependencies)[i] = Utils::StrNDup( |
| reinterpret_cast<const char*>(file_path), file_path_length); |
| free(file_path); |
| free(resolved_uri); |
| } |
| } |
| |
| class ScopedDecompress : public ValueObject { |
| public: |
| ScopedDecompress(const uint8_t** payload, intptr_t* payload_length) |
| : payload_(payload), |
| payload_length_(payload_length), |
| decompressed_(NULL) { |
| DartUtils::MagicNumber payload_type = |
| DartUtils::SniffForMagicNumber(*payload, *payload_length); |
| if (payload_type == DartUtils::kGzipMagicNumber) { |
| int64_t start = Dart_TimelineGetMicros(); |
| intptr_t decompressed_length = 0; |
| Decompress(*payload, *payload_length, &decompressed_, |
| &decompressed_length); |
| int64_t end = Dart_TimelineGetMicros(); |
| Dart_TimelineEvent("Decompress", start, end, Dart_Timeline_Event_Duration, |
| 0, NULL, NULL); |
| *payload_ = decompressed_; |
| *payload_length_ = decompressed_length; |
| } |
| } |
| |
| ~ScopedDecompress() { |
| if (decompressed_ != NULL) { |
| free(decompressed_); |
| } |
| } |
| |
| private: |
| const uint8_t** payload_; |
| intptr_t* payload_length_; |
| uint8_t* decompressed_; |
| }; |
| |
| bool Loader::ProcessResultLocked(Loader* loader, Loader::IOResult* result) { |
| // We have to copy everything we care about out of |result| because after |
| // dropping the lock below |result| may no longer valid. |
| Dart_Handle uri = |
| Dart_NewStringFromCString(reinterpret_cast<char*>(result->uri)); |
| Dart_Handle library_uri = Dart_Null(); |
| if (result->library_uri != NULL) { |
| library_uri = |
| Dart_NewStringFromCString(reinterpret_cast<char*>(result->library_uri)); |
| } |
| |
| AddDependencyLocked(loader, result->resolved_uri); |
| |
| // A negative result tag indicates a loading error occurred in the service |
| // isolate. The payload is a C string of the error message. |
| if (result->tag < 0) { |
| Dart_Handle library = Dart_LookupLibrary(uri); |
| Dart_Handle error = |
| Dart_NewStringFromUTF8(result->payload, result->payload_length); |
| // If a library with the given uri exists, give it a chance to handle |
| // the error. If the load requests stems from a deferred library load, |
| // an IO error is not fatal. |
| if (LibraryHandleError(library, error)) { |
| return true; |
| } |
| // Fall through |
| loader->error_ = Dart_NewUnhandledExceptionError(error); |
| return false; |
| } |
| |
| if (result->tag == _Dart_kImportExtension) { |
| ASSERT(library_uri != Dart_Null()); |
| Dart_Handle library = Dart_LookupLibrary(library_uri); |
| ASSERT(!Dart_IsError(library)); |
| const char* lib_uri = reinterpret_cast<const char*>(result->payload); |
| if (strncmp(lib_uri, "http://", 7) == 0 || |
| strncmp(lib_uri, "https://", 8) == 0) { |
| loader->error_ = Dart_NewApiError( |
| "Cannot load native extensions over http: or https:"); |
| return false; |
| } |
| const char* extension_uri = reinterpret_cast<const char*>(result->uri); |
| const char* lib_path = NULL; |
| if (strncmp(lib_uri, "file://", 7) == 0) { |
| lib_path = DartUtils::RemoveScheme(lib_uri); |
| } else { |
| lib_path = lib_uri; |
| } |
| const char* extension_path = DartUtils::RemoveScheme(extension_uri); |
| if (!File::IsAbsolutePath(extension_path) && |
| PathContainsSeparator(extension_path)) { |
| loader->error_ = DartUtils::NewError( |
| "Native extension path must be absolute, or simply the file name: %s", |
| extension_path); |
| return false; |
| } |
| Dart_Handle result = |
| Extensions::LoadExtension(lib_path, extension_path, library); |
| if (Dart_IsError(result)) { |
| loader->error_ = result; |
| return false; |
| } |
| return true; |
| } |
| |
| // Check for payload and load accordingly. |
| const uint8_t* payload = result->payload; |
| intptr_t payload_length = result->payload_length; |
| |
| // Decompress if gzip'd. |
| ScopedDecompress decompress(&payload, &payload_length); |
| |
| const DartUtils::MagicNumber payload_type = |
| DartUtils::SniffForMagicNumber(payload, payload_length); |
| Dart_Handle source = Dart_Null(); |
| if (payload_type == DartUtils::kUnknownMagicNumber) { |
| source = Dart_NewStringFromUTF8(payload, payload_length); |
| if (Dart_IsError(source)) { |
| loader->error_ = |
| DartUtils::NewError("%s is not a valid UTF-8 script", |
| reinterpret_cast<char*>(result->uri)); |
| return false; |
| } |
| } |
| |
| UNREACHABLE(); |
| return false; |
| } |
| |
| bool Loader::ProcessPayloadResultLocked(Loader* loader, |
| Loader::IOResult* result) { |
| // A negative result tag indicates a loading error occurred in the service |
| // isolate. The payload is a C string of the error message. |
| if (result->tag < 0) { |
| Dart_Handle error = |
| Dart_NewStringFromUTF8(result->payload, result->payload_length); |
| loader->error_ = Dart_NewUnhandledExceptionError(error); |
| return false; |
| } |
| loader->payload_length_ = result->payload_length; |
| loader->payload_ = |
| reinterpret_cast<uint8_t*>(::malloc(loader->payload_length_)); |
| memmove(loader->payload_, result->payload, loader->payload_length_); |
| return true; |
| } |
| |
| bool Loader::ProcessQueueLocked(ProcessResult process_result) { |
| bool hit_error = false; |
| for (intptr_t i = 0; i < results_length(); i++) { |
| if (!hit_error) { |
| hit_error = !(*process_result)(this, &results_[i]); |
| } |
| pending_operations_--; |
| ASSERT(hit_error || (pending_operations_ >= 0)); |
| results_[i].Cleanup(); |
| } |
| results_length_ = 0; |
| return !hit_error; |
| } |
| |
| void Loader::InitForSnapshot(const char* snapshot_uri) { |
| IsolateData* isolate_data = |
| reinterpret_cast<IsolateData*>(Dart_CurrentIsolateData()); |
| ASSERT(isolate_data != NULL); |
| ASSERT(!isolate_data->HasLoader()); |
| // Setup a loader. The constructor does a bunch of leg work. |
| Loader* loader = new Loader(isolate_data); |
| // Send the init message. |
| loader->Init(isolate_data->package_root, isolate_data->packages_file, |
| DartUtils::original_working_directory, snapshot_uri); |
| // Destroy the loader. The destructor does a bunch of leg work. |
| delete loader; |
| } |
| |
| #define RETURN_ERROR(result) \ |
| if (Dart_IsError(result)) return result; |
| |
| Dart_Handle Loader::ReloadNativeExtensions() { |
| Dart_Handle scheme = |
| Dart_NewStringFromCString(DartUtils::kDartExtensionScheme); |
| Dart_Handle extension_imports = Dart_GetImportsOfScheme(scheme); |
| RETURN_ERROR(extension_imports); |
| |
| intptr_t length = -1; |
| Dart_Handle result = Dart_ListLength(extension_imports, &length); |
| RETURN_ERROR(result); |
| Dart_Handle* import_handles = reinterpret_cast<Dart_Handle*>( |
| Dart_ScopeAllocate(sizeof(Dart_Handle) * length)); |
| result = Dart_ListGetRange(extension_imports, 0, length, import_handles); |
| RETURN_ERROR(result); |
| for (intptr_t i = 0; i < length; i += 2) { |
| Dart_Handle importer = import_handles[i]; |
| Dart_Handle importee = import_handles[i + 1]; |
| |
| const char* extension_uri = NULL; |
| result = Dart_StringToCString(Dart_LibraryUrl(importee), &extension_uri); |
| RETURN_ERROR(result); |
| const char* extension_path = DartUtils::RemoveScheme(extension_uri); |
| |
| const char* lib_uri = NULL; |
| result = Dart_StringToCString(Dart_LibraryUrl(importer), &lib_uri); |
| RETURN_ERROR(result); |
| |
| char* lib_path = NULL; |
| if (strncmp(lib_uri, "file://", 7) == 0) { |
| lib_path = DartUtils::DirName(DartUtils::RemoveScheme(lib_uri)); |
| } else { |
| lib_path = strdup(lib_uri); |
| } |
| |
| result = Extensions::LoadExtension(lib_path, extension_path, importer); |
| free(lib_path); |
| RETURN_ERROR(result); |
| } |
| |
| return Dart_True(); |
| } |
| |
| Dart_Handle Loader::SendAndProcessReply(intptr_t tag, |
| Dart_Handle url, |
| uint8_t** payload, |
| intptr_t* payload_length) { |
| IsolateData* isolate_data = |
| reinterpret_cast<IsolateData*>(Dart_CurrentIsolateData()); |
| ASSERT(isolate_data != NULL); |
| ASSERT(!isolate_data->HasLoader()); |
| Loader* loader = NULL; |
| |
| // Setup the loader. The constructor does a bunch of leg work. |
| loader = new Loader(isolate_data); |
| loader->Init(isolate_data->package_root, isolate_data->packages_file, |
| DartUtils::original_working_directory, NULL); |
| ASSERT(loader != NULL); |
| ASSERT(isolate_data->HasLoader()); |
| |
| // Now send a load request to the service isolate. |
| loader->SendRequest(tag, url, Dart_Null()); |
| |
| // Wait for a reply to the load request. |
| loader->BlockUntilComplete(ProcessPayloadResultLocked); |
| |
| // Copy fields from the loader before deleting it. |
| // The payload array itself which was malloced above is freed by |
| // the caller of LoadUrlContents. |
| Dart_Handle error = loader->error(); |
| *payload = loader->payload_; |
| *payload_length = loader->payload_length_; |
| |
| // Destroy the loader. The destructor does a bunch of leg work. |
| delete loader; |
| |
| // An error occurred during loading. |
| if (!Dart_IsNull(error)) { |
| return error; |
| } |
| return Dart_Null(); |
| } |
| |
| Dart_Handle Loader::LoadUrlContents(Dart_Handle url, |
| uint8_t** payload, |
| intptr_t* payload_length) { |
| return SendAndProcessReply(Dart_kScriptTag, url, payload, payload_length); |
| } |
| |
| Dart_Handle Loader::ResolveAsFilePath(Dart_Handle url, |
| uint8_t** payload, |
| intptr_t* payload_length) { |
| return SendAndProcessReply(_Dart_kResolveAsFilePath, url, payload, |
| payload_length); |
| } |
| |
| #if defined(DART_PRECOMPILED_RUNTIME) |
| Dart_Handle Loader::LibraryTagHandler(Dart_LibraryTag tag, |
| Dart_Handle library, |
| Dart_Handle url) { |
| return Dart_Null(); |
| } |
| |
| Dart_Handle Loader::DartColonLibraryTagHandler(Dart_LibraryTag tag, |
| Dart_Handle library, |
| Dart_Handle url, |
| const char* library_url_string, |
| const char* url_string) { |
| return Dart_Null(); |
| } |
| #else |
| static void MallocFinalizer(void* isolate_callback_data, |
| Dart_WeakPersistentHandle handle, |
| void* peer) { |
| free(peer); |
| } |
| |
| Dart_Handle Loader::LibraryTagHandler(Dart_LibraryTag tag, |
| Dart_Handle library, |
| Dart_Handle url) { |
| if (tag == Dart_kCanonicalizeUrl) { |
| Dart_Handle library_url = Dart_LibraryUrl(library); |
| if (Dart_IsError(library_url)) { |
| return library_url; |
| } |
| return Dart_DefaultCanonicalizeUrl(library_url, url); |
| } |
| const char* url_string = NULL; |
| Dart_Handle result = Dart_StringToCString(url, &url_string); |
| if (Dart_IsError(result)) { |
| return result; |
| } |
| if (tag == Dart_kKernelTag) { |
| uint8_t* kernel_buffer = NULL; |
| intptr_t kernel_buffer_size = 0; |
| if (!DFE::TryReadKernelFile(url_string, &kernel_buffer, |
| &kernel_buffer_size)) { |
| return DartUtils::NewError("'%s' is not a kernel file", url_string); |
| } |
| result = Dart_NewExternalTypedData(Dart_TypedData_kUint8, kernel_buffer, |
| kernel_buffer_size); |
| Dart_NewWeakPersistentHandle(result, kernel_buffer, kernel_buffer_size, |
| MallocFinalizer); |
| return result; |
| } |
| if (tag == Dart_kImportExtensionTag) { |
| if (strncmp(url_string, "dart-ext:", 9)) { |
| return DartUtils::NewError( |
| "Native extensions must use the dart-ext: scheme."); |
| } |
| const char* path = DartUtils::RemoveScheme(url_string); |
| |
| const char* lib_uri = NULL; |
| result = Dart_StringToCString(Dart_LibraryResolvedUrl(library), &lib_uri); |
| RETURN_ERROR(result); |
| |
| UriDecoder decoder(lib_uri); |
| lib_uri = decoder.decoded(); |
| |
| char* lib_path = NULL; |
| if (strncmp(lib_uri, "file://", 7) == 0) { |
| lib_path = DartUtils::DirName(lib_uri + 7); |
| } else { |
| lib_path = strdup(lib_uri); |
| } |
| |
| if (!File::IsAbsolutePath(path) && PathContainsSeparator(path)) { |
| return DartUtils::NewError( |
| "Native extension path must be absolute, or simply the file name: " |
| "%s: ", |
| path); |
| } |
| |
| Dart_Handle result = Extensions::LoadExtension(lib_path, path, library); |
| free(lib_path); |
| return result; |
| } |
| if (tag != Dart_kScriptTag) { |
| // Special case for handling dart: imports and parts. |
| // Grab the library's url. |
| Dart_Handle library_url = Dart_LibraryUrl(library); |
| if (Dart_IsError(library_url)) { |
| return library_url; |
| } |
| const char* library_url_string = NULL; |
| result = Dart_StringToCString(library_url, &library_url_string); |
| if (Dart_IsError(result)) { |
| return result; |
| } |
| |
| bool is_dart_scheme_url = DartUtils::IsDartSchemeURL(url_string); |
| bool is_dart_library = DartUtils::IsDartSchemeURL(library_url_string); |
| |
| if (is_dart_scheme_url || is_dart_library) { |
| return DartColonLibraryTagHandler(tag, library, url, library_url_string, |
| url_string); |
| } |
| } |
| if (DartUtils::IsDartExtensionSchemeURL(url_string)) { |
| // Handle early error cases for dart-ext: imports. |
| if (tag != Dart_kImportTag) { |
| return DartUtils::NewError("Dart extensions must use import: '%s'", |
| url_string); |
| } |
| Dart_Handle library_url = Dart_LibraryUrl(library); |
| if (Dart_IsError(library_url)) { |
| return library_url; |
| } |
| } |
| |
| IsolateData* isolate_data = |
| reinterpret_cast<IsolateData*>(Dart_CurrentIsolateData()); |
| ASSERT(isolate_data != NULL); |
| if ((tag == Dart_kScriptTag) && Dart_IsString(library)) { |
| // Update packages file for isolate. |
| const char* packages_file = NULL; |
| Dart_Handle result = Dart_StringToCString(library, &packages_file); |
| if (Dart_IsError(result)) { |
| return result; |
| } |
| isolate_data->UpdatePackagesFile(packages_file); |
| } |
| // Grab this isolate's loader. |
| Loader* loader = NULL; |
| |
| // The outer invocation of the tag handler for this isolate. We make the outer |
| // invocation block and allow any nested invocations to operate in parallel. |
| const bool blocking_call = !isolate_data->HasLoader(); |
| |
| // If we are the outer invocation of the tag handler and the tag is an import |
| // this means that we are starting a deferred library load. |
| const bool is_deferred_import = blocking_call && (tag == Dart_kImportTag); |
| if (!isolate_data->HasLoader()) { |
| // The isolate doesn't have a loader -- this is the outer invocation which |
| // will block. |
| |
| // Setup the loader. The constructor does a bunch of leg work. |
| loader = new Loader(isolate_data); |
| loader->Init(isolate_data->package_root, isolate_data->packages_file, |
| DartUtils::original_working_directory, |
| (tag == Dart_kScriptTag) ? url_string : NULL); |
| } else { |
| ASSERT(tag != Dart_kScriptTag); |
| // The isolate has a loader -- this is an inner invocation that will queue |
| // work with the service isolate. |
| // Use the existing loader. |
| loader = isolate_data->loader(); |
| } |
| ASSERT(loader != NULL); |
| ASSERT(isolate_data->HasLoader()); |
| |
| if (DartUtils::IsDartExtensionSchemeURL(url_string)) { |
| loader->SendImportExtensionRequest(url, Dart_LibraryUrl(library)); |
| } else { |
| if (dfe.CanUseDartFrontend() && dfe.UseDartFrontend() && |
| (tag == Dart_kImportTag)) { |
| // E.g., IsolateMirror.loadUri. |
| char* error = NULL; |
| int exit_code = 0; |
| uint8_t* kernel_buffer = NULL; |
| intptr_t kernel_buffer_size = -1; |
| dfe.CompileAndReadScript(url_string, &kernel_buffer, &kernel_buffer_size, |
| &error, &exit_code, NULL); |
| if (exit_code == 0) { |
| return Dart_LoadLibraryFromKernel(kernel_buffer, kernel_buffer_size); |
| } else if (exit_code == kCompilationErrorExitCode) { |
| Dart_Handle result = Dart_NewCompilationError(error); |
| free(error); |
| return result; |
| } else { |
| Dart_Handle result = Dart_NewApiError(error); |
| free(error); |
| return result; |
| } |
| } else { |
| loader->SendRequest( |
| tag, url, |
| (library != Dart_Null()) ? Dart_LibraryUrl(library) : Dart_Null()); |
| } |
| } |
| |
| if (blocking_call) { |
| // The outer invocation of the tag handler will block here until all nested |
| // invocations complete. |
| loader->BlockUntilComplete(ProcessResultLocked); |
| |
| // Remember the error (if any). |
| Dart_Handle error = loader->error(); |
| // Destroy the loader. The destructor does a bunch of leg work. |
| delete loader; |
| |
| // An error occurred during loading. |
| if (!Dart_IsNull(error)) { |
| if (false && is_deferred_import) { |
| // This blocks handles transitive load errors caused by a deferred |
| // import. Non-transitive load errors are handled above (see callers of |
| // |LibraryHandleError|). To handle the transitive case, we give the |
| // originating deferred library an opportunity to handle it. |
| Dart_Handle deferred_library = Dart_LookupLibrary(url); |
| if (!LibraryHandleError(deferred_library, error)) { |
| // Library did not handle it, return to caller as an unhandled |
| // exception. |
| return Dart_NewUnhandledExceptionError(error); |
| } |
| } else { |
| // We got an error during loading but we aren't loading a deferred |
| // library, return the error to the caller. |
| return error; |
| } |
| } |
| |
| // Finalize loading. This will complete any futures for completed deferred |
| // loads. |
| error = Dart_FinalizeLoading(true); |
| if (Dart_IsError(error)) { |
| return error; |
| } |
| } |
| return Dart_Null(); |
| } |
| |
| Dart_Handle Loader::DartColonLibraryTagHandler(Dart_LibraryTag tag, |
| Dart_Handle library, |
| Dart_Handle url, |
| const char* library_url_string, |
| const char* url_string) { |
| // Handle canonicalization, 'import' and 'part' of 'dart:' libraries. |
| if (tag == Dart_kCanonicalizeUrl) { |
| // These will be handled internally. |
| return url; |
| } |
| // All cases should have been handled above. |
| UNREACHABLE(); |
| return Dart_Null(); |
| } |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| |
| void Loader::InitOnce() { |
| loader_infos_lock_ = new Mutex(); |
| } |
| |
| Mutex* Loader::loader_infos_lock_; |
| Loader::LoaderInfo* Loader::loader_infos_ = NULL; |
| intptr_t Loader::loader_infos_length_ = 0; |
| intptr_t Loader::loader_infos_capacity_ = 0; |
| |
| // Add a mapping from |port| to |isolate_data| (really the loader). When a |
| // native message arrives, we use this map to report the I/O result to the |
| // correct loader. |
| // This happens whenever an isolate begins loading. |
| void Loader::AddLoader(Dart_Port port, IsolateData* isolate_data) { |
| MutexLocker ml(loader_infos_lock_); |
| ASSERT(LoaderForLocked(port) == NULL); |
| if (loader_infos_length_ == loader_infos_capacity_) { |
| // Grow to an initial capacity or double in size. |
| loader_infos_capacity_ = |
| (loader_infos_capacity_ == 0) ? 4 : loader_infos_capacity_ * 2; |
| loader_infos_ = reinterpret_cast<Loader::LoaderInfo*>(realloc( |
| loader_infos_, sizeof(Loader::LoaderInfo) * loader_infos_capacity_)); |
| ASSERT(loader_infos_ != NULL); |
| // Initialize new entries. |
| for (intptr_t i = loader_infos_length_; i < loader_infos_capacity_; i++) { |
| loader_infos_[i].port = ILLEGAL_PORT; |
| loader_infos_[i].isolate_data = NULL; |
| } |
| } |
| ASSERT(loader_infos_length_ < loader_infos_capacity_); |
| loader_infos_[loader_infos_length_].port = port; |
| loader_infos_[loader_infos_length_].isolate_data = isolate_data; |
| loader_infos_length_++; |
| ASSERT(LoaderForLocked(port) != NULL); |
| } |
| |
| // Remove |port| from the map. |
| // This happens once an isolate has finished loading. |
| void Loader::RemoveLoader(Dart_Port port) { |
| MutexLocker ml(loader_infos_lock_); |
| const intptr_t index = LoaderIndexFor(port); |
| ASSERT(index >= 0); |
| const intptr_t last = loader_infos_length_ - 1; |
| ASSERT(last >= 0); |
| if (index != last) { |
| // Swap with the tail. |
| loader_infos_[index] = loader_infos_[last]; |
| } |
| loader_infos_length_--; |
| } |
| |
| intptr_t Loader::LoaderIndexFor(Dart_Port port) { |
| for (intptr_t i = 0; i < loader_infos_length_; i++) { |
| if (loader_infos_[i].port == port) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| Loader* Loader::LoaderForLocked(Dart_Port port) { |
| intptr_t index = LoaderIndexFor(port); |
| if (index < 0) { |
| return NULL; |
| } |
| return loader_infos_[index].isolate_data->loader(); |
| } |
| |
| Loader* Loader::LoaderFor(Dart_Port port) { |
| MutexLocker ml(loader_infos_lock_); |
| return LoaderForLocked(port); |
| } |
| |
| void Loader::NativeMessageHandler(Dart_Port dest_port_id, |
| Dart_CObject* message) { |
| MutexLocker ml(loader_infos_lock_); |
| Loader* loader = LoaderForLocked(dest_port_id); |
| if (loader == NULL) { |
| return; |
| } |
| loader->QueueMessage(message); |
| } |
| |
| } // namespace bin |
| } // namespace dart |