// 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(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(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( 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* dependencies = loader->isolate_data_->dependencies(); if (dependencies == NULL) { return; } dependencies->Add(strdup(resolved_uri)); } void Loader::ResolveDependenciesAsFilePaths() { IsolateData* isolate_data = reinterpret_cast(Dart_CurrentIsolateData()); ASSERT(isolate_data != NULL); MallocGrowableArray* 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(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(result->uri)); Dart_Handle resolved_uri = Dart_NewStringFromCString(reinterpret_cast(result->resolved_uri)); Dart_Handle library_uri = Dart_Null(); if (result->library_uri != NULL) { library_uri = Dart_NewStringFromCString(reinterpret_cast(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(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(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(result->uri)); return false; } } intptr_t tag = result->tag; // No touching. result = NULL; // We must drop the lock here because the tag handler may be recursively // invoked and it will attempt to acquire the lock to queue more work. loader->monitor_->Exit(); Dart_Handle dart_result = Dart_Null(); bool reload_extensions = false; switch (tag) { case Dart_kImportTag: dart_result = Dart_LoadLibrary(uri, resolved_uri, source, 0, 0); break; case Dart_kSourceTag: { ASSERT(library_uri != Dart_Null()); Dart_Handle library = Dart_LookupLibrary(library_uri); ASSERT(!Dart_IsError(library)); dart_result = Dart_LoadSource(library, uri, resolved_uri, source, 0, 0); } break; case Dart_kScriptTag: if (payload_type == DartUtils::kSnapshotMagicNumber) { dart_result = Dart_LoadScriptFromSnapshot(payload, payload_length); reload_extensions = true; } else if (payload_type == DartUtils::kKernelMagicNumber) { // TODO(27590): This code path is only hit when trying to spawn // isolates. We currently do not have support for neither // `Isolate.spawn()` nor `Isolate.spawnUri()` with kernel-based // frontend. dart_result = Dart_LoadScriptFromKernel(payload, payload_length); } else { dart_result = Dart_LoadScript(uri, resolved_uri, source, 0, 0); } break; default: UNREACHABLE(); } // Re-acquire the lock before exiting the function (it was held before entry), loader->monitor_->Enter(); if (Dart_IsError(dart_result)) { // Remember the error if we encountered one. loader->error_ = dart_result; return false; } if (reload_extensions) { dart_result = ReloadNativeExtensions(); if (Dart_IsError(dart_result)) { // Remember the error if we encountered one. loader->error_ = dart_result; return false; } } return true; } 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(::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(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_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(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(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; } else if (tag == Dart_kImportTag) { Builtin::BuiltinLibraryId id = Builtin::FindId(url_string); if (id == Builtin::kInvalidLibrary) { return DartUtils::NewError( "The built-in library '%s' is not available" " on the stand-alone VM.\n", url_string); } return Builtin::LoadLibrary(url, id); } else { ASSERT(tag == Dart_kSourceTag); Builtin::BuiltinLibraryId id = Builtin::FindId(library_url_string); if (id == Builtin::kInvalidLibrary) { return DartUtils::NewError( "The built-in library '%s' is not available" " on the stand-alone VM. Trying to load" " '%s'.\n", library_url_string, url_string); } // Prepend the library URI to form a unique script URI for the part. intptr_t len = snprintf(NULL, 0, "%s/%s", library_url_string, url_string); char* part_uri = reinterpret_cast(malloc(len + 1)); snprintf(part_uri, len + 1, "%s/%s", library_url_string, url_string); Dart_Handle part_uri_obj = DartUtils::NewString(part_uri); Dart_Handle result = Dart_LoadSource(library, part_uri_obj, Dart_Null(), Builtin::PartSource(id, part_uri), 0, 0); free(part_uri); return result; } // 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(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