diff --git a/PRESUBMIT.py b/PRESUBMIT.py index b7a443f458c..d4a4ac2de16 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py @@ -247,7 +247,7 @@ def _CheckClangTidy(input_api, output_api): files = [] for f in input_api.AffectedFiles(): path = f.LocalPath() - if is_cpp_file(path): files.append(path) + if is_cpp_file(path) and os.path.isfile(path): files.append(path) if not files: return [] diff --git a/runtime/bin/builtin.cc b/runtime/bin/builtin.cc index a18ca5644f8..326279a513e 100644 --- a/runtime/bin/builtin.cc +++ b/runtime/bin/builtin.cc @@ -22,14 +22,9 @@ Builtin::builtin_lib_props Builtin::builtin_libraries_[] = { // End marker. {NULL, false}}; -Dart_Port Builtin::load_port_ = ILLEGAL_PORT; const int Builtin::num_libs_ = sizeof(Builtin::builtin_libraries_) / sizeof(Builtin::builtin_lib_props); -Dart_Handle Builtin::PartSource(BuiltinLibraryId id, const char* part_uri) { - UNREACHABLE(); -} - void Builtin::SetNativeResolver(BuiltinLibraryId id) { ASSERT(static_cast(id) >= 0); ASSERT(static_cast(id) < num_libs_); @@ -45,19 +40,6 @@ void Builtin::SetNativeResolver(BuiltinLibraryId id) { } } -Builtin::BuiltinLibraryId Builtin::FindId(const char* url_string) { - int id = 0; - while (true) { - if (builtin_libraries_[id].url_ == NULL) { - return kInvalidLibrary; - } - if (strcmp(url_string, builtin_libraries_[id].url_) == 0) { - return static_cast(id); - } - id++; - } -} - Dart_Handle Builtin::LoadAndCheckLibrary(BuiltinLibraryId id) { ASSERT(static_cast(id) >= 0); ASSERT(static_cast(id) < num_libs_); diff --git a/runtime/bin/builtin.h b/runtime/bin/builtin.h index bfe08d0116c..7605fa0fd73 100644 --- a/runtime/bin/builtin.h +++ b/runtime/bin/builtin.h @@ -33,25 +33,13 @@ class Builtin { kCLILibrary, }; - // Get source of part file specified in 'uri'. - static Dart_Handle PartSource(BuiltinLibraryId id, const char* part_uri); - // Setup native resolver method built in library specified in 'id'. static void SetNativeResolver(BuiltinLibraryId id); - static BuiltinLibraryId FindId(const char* url_string); - // Check if built in library specified in 'id' is already loaded, if not // load it. static Dart_Handle LoadAndCheckLibrary(BuiltinLibraryId id); - static Dart_Handle SetLoadPort(Dart_Port port); - - static Dart_Port LoadPort() { - ASSERT(load_port_ != ILLEGAL_PORT); - return load_port_; - } - private: // Native method support. static Dart_NativeFunction NativeLookup(Dart_Handle name, @@ -60,7 +48,6 @@ class Builtin { static const uint8_t* NativeSymbol(Dart_NativeFunction nf); - static Dart_Port load_port_; static const int num_libs_; typedef struct { diff --git a/runtime/bin/builtin_common.cc b/runtime/bin/builtin_common.cc deleted file mode 100644 index 064d0048565..00000000000 --- a/runtime/bin/builtin_common.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2015, 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 - -#include "include/dart_api.h" - -#include "bin/builtin.h" -#include "bin/dartutils.h" -#include "bin/platform.h" - -// Return the error from the containing function if handle is in error handle. -#define RETURN_IF_ERROR(handle) \ - { \ - Dart_Handle __handle = handle; \ - if (Dart_IsError((__handle))) { \ - return __handle; \ - } \ - } - -namespace dart { -namespace bin { - -Dart_Handle Builtin::SetLoadPort(Dart_Port port) { - Dart_Handle builtin_lib = - Builtin::LoadAndCheckLibrary(Builtin::kBuiltinLibrary); - RETURN_IF_ERROR(builtin_lib); - // Set the _isolateId field. - Dart_Handle result = - Dart_SetField(builtin_lib, DartUtils::NewString("_isolateId"), - Dart_NewInteger(Dart_GetMainPortId())); - RETURN_IF_ERROR(result); - load_port_ = port; - ASSERT(load_port_ != ILLEGAL_PORT); - Dart_Handle field_name = DartUtils::NewString("_loadPort"); - RETURN_IF_ERROR(field_name); - Dart_Handle send_port = Dart_GetField(builtin_lib, field_name); - RETURN_IF_ERROR(send_port); - if (!Dart_IsNull(send_port)) { - // Already created and set. - return Dart_True(); - } - send_port = Dart_NewSendPort(load_port_); - RETURN_IF_ERROR(send_port); - result = Dart_SetField(builtin_lib, field_name, send_port); - RETURN_IF_ERROR(result); - return Dart_True(); -} - -} // namespace bin -} // namespace dart diff --git a/runtime/bin/builtin_impl_sources.gni b/runtime/bin/builtin_impl_sources.gni index f329f034055..97fcb968d28 100644 --- a/runtime/bin/builtin_impl_sources.gni +++ b/runtime/bin/builtin_impl_sources.gni @@ -15,7 +15,6 @@ builtin_impl_sources = [ "crypto_linux.cc", "crypto_macos.cc", "crypto_win.cc", - "builtin_common.cc", "dartutils.cc", "dartutils.h", "directory.cc", diff --git a/runtime/bin/dartutils.cc b/runtime/bin/dartutils.cc index 4331e19f107..be84119bdc1 100644 --- a/runtime/bin/dartutils.cc +++ b/runtime/bin/dartutils.cc @@ -208,7 +208,7 @@ bool DartUtils::IsHttpSchemeURL(const char* url_name) { bool DartUtils::IsDartExtensionSchemeURL(const char* url_name) { static const intptr_t kDartExtensionSchemeLen = strlen(kDartExtensionScheme); - // If the URL starts with "dartext:" then it is considered as a special + // If the URL starts with "dart-ext:" then it is considered as a special // extension library URL which is handled differently from other URLs. return (strncmp(url_name, kDartExtensionScheme, kDartExtensionSchemeLen) == 0); @@ -514,16 +514,6 @@ Dart_Handle DartUtils::PrepareCLILibrary(Dart_Handle cli_lib) { wait_for_event_handle); } -Dart_Handle DartUtils::SetupServiceLoadPort() { - // Wait for the service isolate to initialize the load port. - Dart_Port load_port = Dart_ServiceWaitForLoadPort(); - if (load_port == ILLEGAL_PORT) { - return Dart_NewUnhandledExceptionError( - NewDartUnsupportedError("Service did not return load port.")); - } - return Builtin::SetLoadPort(load_port); -} - Dart_Handle DartUtils::SetupPackageConfig(const char* packages_config) { Dart_Handle result = Dart_Null(); diff --git a/runtime/bin/dartutils.h b/runtime/bin/dartutils.h index 10cfa711082..a0744ab9fd0 100644 --- a/runtime/bin/dartutils.h +++ b/runtime/bin/dartutils.h @@ -151,7 +151,6 @@ class DartUtils { static Dart_Handle MakeUint8Array(const uint8_t* buffer, intptr_t length); static Dart_Handle PrepareForScriptLoading(bool is_service_isolate, bool trace_loading); - static Dart_Handle SetupServiceLoadPort(); static Dart_Handle SetupPackageConfig(const char* packages_file); static Dart_Handle SetupIOLibrary(const char* namespc_path, diff --git a/runtime/bin/loader.cc b/runtime/bin/loader.cc index c85712713e0..433e1f2cafa 100644 --- a/runtime/bin/loader.cc +++ b/runtime/bin/loader.cc @@ -19,237 +19,35 @@ 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_kInitLoader = 4; -static const intptr_t _Dart_kImportExtension = 9; +void Loader::InitForSnapshot(const char* snapshot_uri, + IsolateData* isolate_data) { + ASSERT(isolate_data != NULL); -Loader::Loader(IsolateData* isolate_data) - : port_(ILLEGAL_PORT), - isolate_data_(isolate_data), - error_(Dart_Null()), - monitor_(), - pending_operations_(0), - results_(NULL), - results_length_(0), - results_capacity_(0), - payload_(NULL), - payload_length_(0) { - ASSERT(isolate_data_ != NULL); - port_ = Dart_NewNativePort("Loader", Loader::NativeMessageHandler, false); - isolate_data_->set_loader(this); - AddLoader(port_, isolate_data_); + Dart_Handle result = + Loader::Init(isolate_data->packages_file(), + DartUtils::original_working_directory, snapshot_uri); + ASSERT(!Dart_IsError(result)); } -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; - 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); - - 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) +// Initialize package resolution state. +Dart_Handle Loader::Init(const char* packages_file, + const char* working_directory, + const char* root_script_uri) { + const int kNumArgs = 3; + Dart_Handle dart_args[kNumArgs]; + dart_args[0] = (packages_file == NULL) ? Dart_Null() - : Dart_NewStringFromCString(package_root)); - Dart_ListSetAt(request, 5, - (packages_file == NULL) + : Dart_NewStringFromCString(packages_file); + dart_args[1] = Dart_NewStringFromCString(working_directory); + dart_args[2] = (root_script_uri == 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; + : Dart_NewStringFromCString(root_script_uri); + return Dart_Invoke(DartUtils::LookupBuiltinLib(), + DartUtils::NewString("_Init"), kNumArgs, dart_args); } static bool PathContainsSeparator(const char* path) { @@ -258,156 +56,47 @@ static bool PathContainsSeparator(const char* path) { (strstr(path, File::PathSeparator()) != NULL)); } -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 library_uri = Dart_Null(); - if (result->library_uri != NULL) { - library_uri = - Dart_NewStringFromCString(reinterpret_cast(result->library_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 (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; - } - } - - UNREACHABLE(); - return false; -} - -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) { - 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->isolate_group_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::LoadImportExtension(const char* url_string, + Dart_Handle library) { + const char* lib_uri_str = NULL; + Dart_Handle lib_uri = Dart_LibraryResolvedUrl(library); + ASSERT(!Dart_IsError(lib_uri)); + Dart_Handle result = Dart_StringToCString(lib_uri, &lib_uri_str); + RETURN_ERROR(result); + + UriDecoder decoder(lib_uri_str); + lib_uri_str = decoder.decoded(); + + if (strncmp(lib_uri_str, "http://", 7) == 0 || + strncmp(lib_uri_str, "https://", 8) == 0 || + strncmp(lib_uri_str, "data://", 7) == 0) { + return DartUtils::NewError( + "Cannot load native extensions over http: or https: or data: %s", + lib_uri_str); + } + + char* lib_path = NULL; + if (strncmp(lib_uri_str, "file://", 7) == 0) { + lib_path = DartUtils::DirName(lib_uri_str + 7); + } else { + lib_path = strdup(lib_uri_str); + } + + const char* path = DartUtils::RemoveScheme(url_string); + if (!File::IsAbsolutePath(path) && PathContainsSeparator(path)) { + return DartUtils::NewError( + "Native extension path must be absolute, or simply the file name: %s", + path); + } + + result = Extensions::LoadExtension(lib_path, path, library); + free(lib_path); + return result; +} + Dart_Handle Loader::ReloadNativeExtensions() { Dart_Handle scheme = Dart_NewStringFromCString(DartUtils::kDartExtensionScheme); @@ -449,10 +138,6 @@ Dart_Handle Loader::ReloadNativeExtensions() { return Dart_True(); } -IsolateGroupData* Loader::isolate_group_data() { - return isolate_data_->isolate_group_data(); -} - #if defined(DART_PRECOMPILED_RUNTIME) Dart_Handle Loader::LibraryTagHandler(Dart_LibraryTag tag, Dart_Handle library, @@ -505,208 +190,38 @@ Dart_Handle Loader::LibraryTagHandler(Dart_LibraryTag tag, return result; } if (tag == Dart_kImportExtensionTag) { - if (strncmp(url_string, "dart-ext:", 9) != 0) { + if (!DartUtils::IsDartExtensionSchemeURL(url_string)) { return DartUtils::NewError( - "Native extensions must use the dart-ext: scheme."); + "Native extensions must use the dart-ext: scheme : %s", url_string); } - 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); + return Loader::LoadImportExtension(url_string, library); + } + 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 { - 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 (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; + Dart_Handle result = Dart_NewApiError(error); + free(error); + return result; } } - - auto isolate_data = reinterpret_cast(Dart_CurrentIsolateData()); - ASSERT(isolate_data != NULL); - - // Grab this isolate's loader. - Loader* loader = NULL; - - 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->isolate_group_data()->package_root, - isolate_data->packages_file(), - DartUtils::original_working_directory, NULL); - } else { - // 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()); - } - } - - // 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)) { - // We got an error during loading, return the error to the caller. - return error; - } - - // Finalize loading. - error = Dart_FinalizeLoading(true); - if (Dart_IsError(error)) { - return error; - } - return Dart_Null(); + return DartUtils::NewError("Invalid tag : %d '%s'", tag, url_string); } #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 diff --git a/runtime/bin/loader.h b/runtime/bin/loader.h index 54af63fa1ac..bb2576dc14c 100644 --- a/runtime/bin/loader.h +++ b/runtime/bin/loader.h @@ -17,9 +17,6 @@ namespace bin { class Loader { public: - explicit Loader(IsolateData* isolate_data); - ~Loader(); - static void InitForSnapshot(const char* snapshot_uri, IsolateData* isolate_data); @@ -30,101 +27,18 @@ class Loader { Dart_Handle library, Dart_Handle url); - IsolateGroupData* isolate_group_data(); - - Dart_Handle error() const { return error_; } - static void InitOnce(); private: - // The port assigned to our native message handler. - Dart_Port port_; - // Each Loader is associated with an Isolate via its IsolateData. - IsolateData* isolate_data_; - // Remember the first error that occurs during loading. - Dart_Handle error_; - // This monitor is used to protect the pending operations count and the - // I/O result queue. - Monitor monitor_; + static Dart_Handle Init(const char* packages_file, + const char* working_directory, + const char* root_script_uri); - // The number of operations dispatched to the service isolate for loading. - // Must be accessed with monitor_ held. - intptr_t pending_operations_; + static Dart_Handle LoadImportExtension(const char* url_string, + Dart_Handle library); - // The result of an I/O request to the service isolate. Payload is either - // a UInt8Array or a C string containing an error message. - struct IOResult { - uint8_t* payload; - intptr_t payload_length; - char* library_uri; - char* uri; - char* resolved_uri; - int8_t tag; - - void Setup(Dart_CObject* message); - void Cleanup(); - }; - // An array of I/O results queued from the service isolate. - IOResult* results_; - intptr_t results_length_; - intptr_t results_capacity_; - uint8_t* payload_; - intptr_t payload_length_; - typedef bool (*ProcessResult)(Loader* loader, IOResult* result); - - intptr_t results_length() { - return *static_cast(&results_length_); - } - - // Send the loader init request to the service isolate. - void Init(const char* package_root, - const char* packages_file, - const char* working_directory, - const char* root_script_uri); - - // Send a request for a dart-ext: import to the service isolate. - void SendImportExtensionRequest(Dart_Handle url, Dart_Handle library_url); - - // Send a request from the tag handler to the service isolate. - void SendRequest(intptr_t tag, Dart_Handle url, Dart_Handle library_url); - - /// Queue |message| and notify the loader that a message is available. - void QueueMessage(Dart_CObject* message); - - /// Blocks the caller until the loader is finished. - void BlockUntilComplete(ProcessResult process_result); - - /// Returns false if |result| is an error and the loader should quit. - static bool ProcessResultLocked(Loader* loader, IOResult* result); - - /// Returns false if an error occurred and the loader should quit. - bool ProcessQueueLocked(ProcessResult process_result); - - // We use one native message handler callback for N loaders. The native - // message handler callback provides us with the Dart_Port which we use as a - // key into our map of active loaders from |port| to |isolate_data|. - - // Static information to map Dart_Port back to the isolate in question. - struct LoaderInfo { - Dart_Port port; - IsolateData* isolate_data; - }; - - // The map of active loaders. - static Mutex* loader_infos_lock_; - static LoaderInfo* loader_infos_; - static intptr_t loader_infos_length_; - static intptr_t loader_infos_capacity_; - - static void AddLoader(Dart_Port port, IsolateData* data); - static void RemoveLoader(Dart_Port port); - static intptr_t LoaderIndexFor(Dart_Port port); - static Loader* LoaderFor(Dart_Port port); - static Loader* LoaderForLocked(Dart_Port port); - - // This is the global callback for the native message handlers. - static void NativeMessageHandler(Dart_Port dest_port_id, - Dart_CObject* message); + DISALLOW_ALLOCATION(); + DISALLOW_IMPLICIT_CONSTRUCTORS(Loader); }; } // namespace bin diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc index 7678229a8a7..8225e8f3f2f 100644 --- a/runtime/bin/main.cc +++ b/runtime/bin/main.cc @@ -191,13 +191,6 @@ static Dart_Handle SetupCoreLibraries(Dart_Isolate isolate, result = DartUtils::PrepareForScriptLoading(false, Options::trace_loading()); if (Dart_IsError(result)) return result; - if (Dart_IsVMFlagSet("support_service") || !Dart_IsPrecompiledRuntime()) { - // Set up the load port provided by the service isolate so that we can - // load scripts. - result = DartUtils::SetupServiceLoadPort(); - if (Dart_IsError(result)) return result; - } - // Setup packages config if specified. result = DartUtils::SetupPackageConfig(packages_file); if (Dart_IsError(result)) return result; diff --git a/runtime/bin/vmservice/loader.dart b/runtime/bin/vmservice/loader.dart deleted file mode 100644 index 35a5fdc5dbc..00000000000 --- a/runtime/bin/vmservice/loader.dart +++ /dev/null @@ -1,865 +0,0 @@ -// Copyright (c) 2015, 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. - -// @dart = 2.6 - -part of vmservice_io; - -_sanitizeWindowsPath(path) { - // 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 - - if (_isWindows == false) { - // Do nothing when not running Windows. - return path; - } - - var fixedPath = "${path.replaceAll('\\', '/')}"; - - if ((path.length > 2) && (path[1] == ':')) { - // Path begins with a drive letter. - return '/$fixedPath'; - } - - return fixedPath; -} - -@pragma("vm:entry-point") -bool _traceLoading = false; - -// State associated with the isolate that is used for loading. -class IsolateLoaderState extends IsolateEmbedderData { - IsolateLoaderState(this.isolateId); - - final int isolateId; - bool _dead = false; - SendPort sp; - - void init(String packageRootFlag, String packagesConfigFlag, - String workingDirectory, String rootScript) { - if (_dead) { - return; - } - // _workingDirectory must be set first. - _workingDirectory = new Uri.directory(workingDirectory); - if (rootScript != null) { - _rootScript = Uri.parse(rootScript); - } - // If the --packages flag was passed. - if (packagesConfigFlag != null) { - _setPackagesConfig(packagesConfigFlag); - } - } - - void updatePackageMap(String packagesConfigFlag) { - if (packagesConfigFlag == null) { - return; - } - _packageMap = null; - _setPackagesConfig(packagesConfigFlag); - } - - void cleanup() { - _dead = true; - if (_packagesPort != null) { - _packagesPort.close(); - _packagesPort = null; - } - } - - // The working directory when the embedder started. - Uri _workingDirectory; - - // The root script's uri. - Uri _rootScript; - - // Packages are either resolved looking up in a map or resolved from within a - // package root. - bool get _packagesReady => - (_packageRoot != null) || - (_packageMap != null) || - (_packageError != null); - - // Error string set if there was an error resolving package configuration. - // For example not finding a .packages file or packages/ directory, malformed - // .packages file or any other related error. - String _packageError = null; - - // The directory to look in to resolve "package:" scheme URIs. By default it - // is the 'packages' directory right next to the script. - Uri _packageRoot = null; - - // The map describing how certain package names are mapped to Uris. - Uri _packageConfig = null; - Map _packageMap = null; - - _setPackagesConfig(String packagesParam) { - var packagesName = _sanitizeWindowsPath(packagesParam); - var packagesUri = Uri.parse(packagesName); - if (packagesUri.scheme == '') { - // Script does not have a scheme, assume that it is a path, - // resolve it against the working directory. - packagesUri = _workingDirectory.resolveUri(packagesUri); - } - _requestPackagesMap(packagesUri); - _pendingPackageLoads.add(() { - // Dummy action. - }); - } - - // Handling of access to the package root or package map from user code. - _triggerPackageResolution(action) { - if (_packagesReady) { - // Packages are ready. Execute the action now. - action(); - } else { - if (_pendingPackageLoads.isEmpty) { - // Package resolution has not been setup yet, and this is the first - // request for package resolution & loading. - _requestPackagesMap(); - } - // Register the action for when the package resolution is ready. - _pendingPackageLoads.add(action); - } - } - - // A list of callbacks which should be invoked after the package map has been - // loaded. - List _pendingPackageLoads = []; - - // Given a uri with a 'package' scheme, return a Uri that is prefixed with - // the package root or resolved relative to the package configuration. - Uri _resolvePackageUri(Uri uri) { - assert(uri.scheme == "package"); - assert(_packagesReady); - - if (uri.host.isNotEmpty) { - var path = '${uri.host}${uri.path}'; - var right = 'package:$path'; - var wrong = 'package://$path'; - - throw "URIs using the 'package:' scheme should look like " - "'$right', not '$wrong'."; - } - - var packageNameEnd = uri.path.indexOf('/'); - if (packageNameEnd == 0) { - // Package URIs must have a non-empty package name (not start with "/"). - throw "URIS using the 'package:' scheme should look like " - "'package:packageName${uri.path}', not 'package:${uri.path}'"; - } - if (_traceLoading) { - _log('Resolving package with uri path: ${uri.path}'); - } - var resolvedUri; - if (_packageError != null) { - if (_traceLoading) { - _log("Resolving package with pending resolution error: $_packageError"); - } - throw _packageError; - } else if (_packageRoot != null) { - resolvedUri = _packageRoot.resolve(uri.path); - } else { - if (packageNameEnd < 0) { - // Package URIs must have a path after the package name, even if it's - // just "/". - throw "URIS using the 'package:' scheme should look like " - "'package:${uri.path}/', not 'package:${uri.path}'"; - } - var packageName = uri.path.substring(0, packageNameEnd); - var mapping = _packageMap[packageName]; - if (_traceLoading) { - _log("Mapped '$packageName' package to '$mapping'"); - } - if (mapping == null) { - throw "No mapping for '$packageName' package when resolving '$uri'."; - } - var path; - assert(uri.path.length > packageName.length); - path = uri.path.substring(packageName.length + 1); - if (_traceLoading) { - _log("Path to be resolved in package: $path"); - } - resolvedUri = mapping.resolve(path); - } - if (_traceLoading) { - _log("Resolved '$uri' to '$resolvedUri'."); - } - return resolvedUri; - } - - RawReceivePort _packagesPort; - - void _requestPackagesMap([Uri packageConfig]) { - if (_packagesPort != null) { - // Already scheduled. - return; - } - // Create a port to receive the packages map on. - _packagesPort = new RawReceivePort(_handlePackagesReply); - var sp = _packagesPort.sendPort; - - if (packageConfig != null) { - // Explicitly specified .packages path. - _handlePackagesRequest(sp, _traceLoading, -2, packageConfig); - } else { - // Search for .packages starting at the root script. - _handlePackagesRequest(sp, _traceLoading, -1, _rootScript); - } - - if (_traceLoading) { - _log("Requested packages map for '$_rootScript'."); - } - } - - void _handlePackagesReply(msg) { - if (_packagesPort == null) { - return; - } - // Make sure to close the _packagePort before any other action. - _packagesPort.close(); - _packagesPort = null; - - if (_traceLoading) { - _log("Got packages reply: $msg"); - } - if (msg is String) { - if (_traceLoading) { - _log("Got failure response on package port: '$msg'"); - } - // Remember the error message. - _packageError = msg; - } else if (msg is List) { - if (msg.length == 1) { - if (_traceLoading) { - _log("Received package root: '${msg[0]}'"); - } - _packageRoot = Uri.parse(msg[0]); - } else { - // First entry contains the location of the loaded .packages file. - assert((msg.length % 2) == 0); - assert(msg.length >= 2); - assert(msg[1] == null); - _packageConfig = Uri.parse(msg[0]); - _packageMap = new Map(); - for (var i = 2; i < msg.length; i += 2) { - // TODO(iposva): Complain about duplicate entries. - _packageMap[msg[i]] = Uri.parse(msg[i + 1]); - } - if (_traceLoading) { - _log("Setup package map: $_packageMap"); - } - } - } else { - _packageError = "Bad type of packages reply: ${msg.runtimeType}"; - if (_traceLoading) { - _log(_packageError); - } - } - - // Resolve all pending package loads now that we know how to resolve them. - while (_pendingPackageLoads.length > 0) { - // Order does not matter as we queue all of the requests up right now. - var req = _pendingPackageLoads.removeLast(); - // Call the registered closure, to handle the delayed action. - req(); - } - // Reset the pending package loads to empty. So that we eventually can - // finish loading. - _pendingPackageLoads = []; - } -} - -_log(msg) { - print("% $msg"); -} - -var _httpClient; - -// Send a response to the requesting isolate. -void _sendResourceResponse(SendPort sp, int tag, Uri uri, Uri resolvedUri, - String libraryUrl, dynamic data) { - assert((data is List) || (data is String)); - var msg = new List(5); - if (data is String) { - // We encountered an error, flip the sign of the tag to indicate that. - tag = -tag; - if (libraryUrl == null) { - data = 'Could not load "$uri": $data'; - } else { - data = 'Could not import "$uri" from "$libraryUrl": $data'; - } - } - msg[0] = tag; - msg[1] = uri.toString(); - msg[2] = resolvedUri.toString(); - msg[3] = libraryUrl; - msg[4] = data; - sp.send(msg); -} - -// Send a response to the requesting isolate. -void _sendExtensionImportResponse( - SendPort sp, Uri uri, String libraryUrl, String resolvedUri) { - var msg = new List(5); - int tag = _Dart_kImportExtension; - if (resolvedUri == null) { - // We could not resolve the dart-ext: uri. - tag = -tag; - resolvedUri = 'Could not resolve "$uri" from "$libraryUrl"'; - } - msg[0] = tag; - msg[1] = uri.toString(); - msg[2] = resolvedUri; - msg[3] = libraryUrl; - msg[4] = resolvedUri; - sp.send(msg); -} - -// Handling of packages requests. Finding and parsing of .packages file or -// packages/ directories. -const _LF = 0x0A; -const _CR = 0x0D; -const _SPACE = 0x20; -const _HASH = 0x23; -const _DOT = 0x2E; -const _COLON = 0x3A; -const _DEL = 0x7F; - -const _invalidPackageNameChars = const [ - true, // space - false, // ! - true, // " - true, // # - false, // $ - true, // % - false, // & - false, // ' - false, // ( - false, // ) - false, // * - false, // + - false, // , - false, // - - false, // . - true, // / - false, // 0 - false, // 1 - false, // 2 - false, // 3 - false, // 4 - false, // 5 - false, // 6 - false, // 7 - false, // 8 - false, // 9 - true, // : - false, // ; - true, // < - false, // = - true, // > - true, // ? - false, // @ - false, // A - false, // B - false, // C - false, // D - false, // E - false, // F - false, // G - false, // H - false, // I - false, // J - false, // K - false, // L - false, // M - false, // N - false, // O - false, // P - false, // Q - false, // R - false, // S - false, // T - false, // U - false, // V - false, // W - false, // X - false, // Y - false, // Z - true, // [ - true, // \ - true, // ] - true, // ^ - false, // _ - true, // ` - false, // a - false, // b - false, // c - false, // d - false, // e - false, // f - false, // g - false, // h - false, // i - false, // j - false, // k - false, // l - false, // m - false, // n - false, // o - false, // p - false, // q - false, // r - false, // s - false, // t - false, // u - false, // v - false, // w - false, // x - false, // y - false, // z - true, // { - true, // | - true, // } - false, // ~ - true, // DEL -]; - -_parsePackagesFile( - SendPort sp, bool traceLoading, Uri packagesFile, List data) { - // The first entry contains the location of the identified .packages file - // instead of a mapping. - var result = [packagesFile.toString(), null]; - var index = 0; - var len = data.length; - while (index < len) { - var start = index; - var char = data[index]; - if ((char == _CR) || (char == _LF)) { - // Skipping empty lines. - index++; - continue; - } - - // Identify split within the line and end of the line. - var separator = -1; - var end = len; - // Verifying validity of package name while scanning the line. - var nonDot = false; - var invalidPackageName = false; - - // Scan to the end of the line or data. - while (index < len) { - char = data[index++]; - // If we have not reached the separator yet, determine whether we are - // scanning legal package name characters. - if (separator == -1) { - if ((char == _COLON)) { - // The first colon on a line is the separator between package name and - // related URI. - separator = index - 1; - } else { - // Still scanning the package name part. Check for the validity of - // the characters. - nonDot = nonDot || (char != _DOT); - invalidPackageName = invalidPackageName || - (char < _SPACE) || - (char > _DEL) || - _invalidPackageNameChars[char - _SPACE]; - } - } - // Identify end of line. - if ((char == _CR) || (char == _LF)) { - end = index - 1; - break; - } - } - - // No further handling needed for comment lines. - if (data[start] == _HASH) { - if (traceLoading) { - _log("Skipping comment in $packagesFile:\n" - "${new String.fromCharCodes(data, start, end)}"); - } - continue; - } - - // Check for a badly formatted line, starting with a ':'. - if (separator == start) { - var line = new String.fromCharCodes(data, start, end); - if (traceLoading) { - _log("Line starts with ':' in $packagesFile:\n" - "$line"); - } - sp.send("Missing package name in $packagesFile:\n" - "$line"); - return; - } - - // Ensure there is a separator on the line. - if (separator == -1) { - var line = new String.fromCharCodes(data, start, end); - if (traceLoading) { - _log("Line has no ':' in $packagesFile:\n" - "$line"); - } - sp.send("Missing ':' separator in $packagesFile:\n" - "$line"); - return; - } - - var packageName = new String.fromCharCodes(data, start, separator); - - // Check for valid package name. - if (invalidPackageName || !nonDot) { - var line = new String.fromCharCodes(data, start, end); - if (traceLoading) { - _log("Invalid package name $packageName in $packagesFile"); - } - sp.send("Invalid package name '$packageName' in $packagesFile:\n" - "$line"); - return; - } - - if (traceLoading) { - _log("packageName: $packageName"); - } - var packageUri = new String.fromCharCodes(data, separator + 1, end); - if (traceLoading) { - _log("original packageUri: $packageUri"); - } - // Ensure the package uri ends with a /. - if (!packageUri.endsWith("/")) { - packageUri = "$packageUri/"; - } - packageUri = packagesFile.resolve(packageUri).toString(); - if (traceLoading) { - _log("mapping: $packageName -> $packageUri"); - } - result.add(packageName); - result.add(packageUri); - } - - if (traceLoading) { - _log("Parsed packages file at $packagesFile. Sending:\n$result"); - } - sp.send(result); -} - -_loadPackagesFile(SendPort sp, bool traceLoading, Uri packagesFile) async { - try { - var data = await new File.fromUri(packagesFile).readAsBytes(); - if (traceLoading) { - _log("Loaded packages file from $packagesFile:\n" - "${new String.fromCharCodes(data)}"); - } - _parsePackagesFile(sp, traceLoading, packagesFile, data); - } catch (e, s) { - if (traceLoading) { - _log("Error loading packages: $e\n$s"); - } - sp.send("Uncaught error ($e) loading packages file."); - } -} - -_findPackagesFile(SendPort sp, bool traceLoading, Uri base) async { - try { - // Walk up the directory hierarchy to check for the existence of - // .packages files in parent directories and for the existence of a - // packages/ directory on the first iteration. - var dir = new File.fromUri(base).parent; - var prev = null; - // Keep searching until we reach the root. - while ((prev == null) || (prev.path != dir.path)) { - // Check for the existence of a .packages file and if it exists try to - // load and parse it. - var dirUri = dir.uri; - var packagesFile = dirUri.resolve(".packages"); - if (traceLoading) { - _log("Checking for $packagesFile file."); - } - var exists = await new File.fromUri(packagesFile).exists(); - if (traceLoading) { - _log("$packagesFile exists: $exists"); - } - if (exists) { - _loadPackagesFile(sp, traceLoading, packagesFile); - return; - } - // Move up one level. - prev = dir; - dir = dir.parent; - } - - // No .packages file was found. - if (traceLoading) { - _log("Could not resolve a package location from $base"); - } - sp.send("Could not resolve a package location for base at $base"); - } catch (e, s) { - if (traceLoading) { - _log("Error loading packages: $e\n$s"); - } - sp.send("Uncaught error ($e) loading packages file."); - } -} - -Future _loadHttpPackagesFile( - SendPort sp, bool traceLoading, Uri resource) async { - try { - if (_httpClient == null) { - _httpClient = new HttpClient()..maxConnectionsPerHost = 6; - } - if (traceLoading) { - _log("Fetching packages file from '$resource'."); - } - var req = await _httpClient.getUrl(resource); - var rsp = await req.close(); - var builder = new BytesBuilder(copy: false); - await for (var bytes in rsp) { - builder.add(bytes); - } - if (rsp.statusCode != 200) { - if (traceLoading) { - _log("Got status ${rsp.statusCode} fetching '$resource'."); - } - return false; - } - var data = builder.takeBytes(); - if (traceLoading) { - _log("Loaded packages file from '$resource':\n" - "${new String.fromCharCodes(data)}"); - } - _parsePackagesFile(sp, traceLoading, resource, data); - } catch (e, s) { - if (traceLoading) { - _log("Error loading packages file from '$resource': $e\n$s"); - } - sp.send("Uncaught error ($e) loading packages file from '$resource'."); - } - return false; -} - -_loadPackagesData(sp, traceLoading, resource) { - try { - var data = resource.data; - var mime = data.mimeType; - if (mime != "text/plain") { - throw "MIME-type must be text/plain: $mime given."; - } - var charset = data.charset; - if ((charset != "utf-8") && (charset != "US-ASCII")) { - // The C++ portion of the embedder assumes UTF-8. - throw "Only utf-8 or US-ASCII encodings are supported: $charset given."; - } - _parsePackagesFile(sp, traceLoading, resource, data.contentAsBytes()); - } catch (e) { - sp.send("Uncaught error ($e) loading packages data."); - } -} - -// This code used to exist in a second isolate and so it uses a SendPort to -// report it's return value. This could be refactored so that it returns it's -// value and the caller could wait on the future rather than a message on -// SendPort. -_handlePackagesRequest( - SendPort sp, bool traceLoading, int tag, Uri resource) async { - try { - if (tag == -1) { - if (resource.scheme == '' || resource.scheme == 'file') { - _findPackagesFile(sp, traceLoading, resource); - } else if ((resource.scheme == 'http') || (resource.scheme == 'https')) { - // Try to load the .packages file next to the resource. - var packagesUri = resource.resolve(".packages"); - var exists = await _loadHttpPackagesFile(sp, traceLoading, packagesUri); - if (!exists) { - // Loading of the .packages file failed for http/https based scripts - sp.send([null]); - } - } else { - sp.send("Unsupported scheme used to locate .packages file: " - "'$resource'."); - } - } else if (tag == -2) { - if (traceLoading) { - _log("Handling load of packages map: '$resource'."); - } - if (resource.scheme == '' || resource.scheme == 'file') { - var exists = await new File.fromUri(resource).exists(); - if (exists) { - _loadPackagesFile(sp, traceLoading, resource); - } else { - sp.send("Packages file '$resource' not found."); - } - } else if ((resource.scheme == 'http') || (resource.scheme == 'https')) { - var exists = await _loadHttpPackagesFile(sp, traceLoading, resource); - if (!exists) { - sp.send("Packages file '$resource' not found."); - } - } else if (resource.scheme == 'data') { - _loadPackagesData(sp, traceLoading, resource); - } else { - sp.send("Unknown scheme (${resource.scheme}) for package file at " - "'$resource'."); - } - } else { - sp.send("Unknown packages request tag: $tag for '$resource'."); - } - } catch (e, s) { - if (traceLoading) { - _log("Error handling packages request: $e\n$s"); - } - sp.send("Uncaught error ($e) handling packages request."); - } -} - -// Shutdown all active loaders by sending an error message. -void shutdownLoaders() { - String message = 'Service shutdown'; - if (_httpClient != null) { - _httpClient.close(force: true); - _httpClient = null; - } - isolateEmbedderData.values.toList().forEach((ied) { - IsolateLoaderState ils = ied; - ils.cleanup(); - assert(ils.sp != null); - _sendResourceResponse(ils.sp, 1, null, null, null, message); - }); -} - -// See Dart_LibraryTag in dart_api.h -const _Dart_kCanonicalizeUrl = 0; // Canonicalize the URL. - -// Extra requests. Keep these in sync between loader.dart and builtin.dart. -const _Dart_kInitLoader = 4; // Initialize the loader. -const _Dart_kGetPackageConfigUri = 7; // Uri of the .packages file. -const _Dart_kResolvePackageUri = 8; // Resolve a package: uri. - -// Extra requests. Keep these in sync between loader.dart and loader.cc. -const _Dart_kImportExtension = 9; // Import a dart-ext: file. - -// External entry point for loader requests. -_processLoadRequest(request) { - assert(request is List); - assert(request.length > 4); - - // Should we trace loading? - bool traceLoading = request[0]; - - // This is the sending isolate's Dart_GetMainPortId(). - int isolateId = request[1]; - - // The tag describing the operation. - int tag = request[2]; - - // The send port to send the response on. - SendPort sp = request[3]; - - // Grab the loader state for the requesting isolate. - IsolateLoaderState loaderState = isolateEmbedderData[isolateId]; - - // We are either about to initialize the loader, or, we already have. - assert((tag == _Dart_kInitLoader) || (loaderState != null)); - - // Handle the request specified in the tag. - switch (tag) { - case _Dart_kInitLoader: - { - String packageRoot = request[4]; - String packagesFile = request[5]; - String workingDirectory = request[6]; - String rootScript = request[7]; - bool isReloading = request[8]; - if (loaderState == null) { - loaderState = new IsolateLoaderState(isolateId); - isolateEmbedderData[isolateId] = loaderState; - loaderState.init( - packageRoot, packagesFile, workingDirectory, rootScript); - } else if (isReloading) { - loaderState.updatePackageMap(packagesFile); - } - loaderState.sp = sp; - assert(isolateEmbedderData[isolateId] == loaderState); - } - break; - case _Dart_kGetPackageConfigUri: - loaderState._triggerPackageResolution(() { - // Respond with the packages config (if any) after package resolution. - sp.send(loaderState._packageConfig); - }); - break; - case _Dart_kResolvePackageUri: - Uri uri = Uri.parse(request[4]); - loaderState._triggerPackageResolution(() { - // Respond with the resolved package uri after package resolution. - Uri resolvedUri; - try { - resolvedUri = loaderState._resolvePackageUri(uri); - } catch (e, s) { - if (traceLoading) { - _log("Exception ($e) when resolving package URI: $uri"); - } - resolvedUri = null; - } - sp.send(resolvedUri); - }); - break; - case _Dart_kImportExtension: - Uri uri = Uri.parse(request[4]); - String libraryUri = request[5]; - // Strip any filename off of the libraryUri's path. - int index = libraryUri.lastIndexOf('/'); - var path; - if (index == -1) { - path = './'; - } else { - path = libraryUri.substring(0, index + 1); - } - var pathUri = Uri.parse(path); - switch (pathUri.scheme) { - case '': - case 'file': - _sendExtensionImportResponse( - sp, uri, libraryUri, pathUri.toFilePath()); - break; - case 'data': - case 'http': - case 'https': - _sendExtensionImportResponse(sp, uri, libraryUri, pathUri.toString()); - break; - case 'package': - // Start package resolution. - loaderState._triggerPackageResolution(() { - // Attempt to find the fully resolved uri of [path]. - Uri resolvedUri; - try { - resolvedUri = loaderState._resolvePackageUri(pathUri); - } catch (e, s) { - if (traceLoading) { - _log("Exception ($e) when resolving package URI: $uri"); - } - resolvedUri = null; - } - _sendExtensionImportResponse( - sp, uri, libraryUri, resolvedUri.toString()); - }); - break; - default: - if (traceLoading) { - _log('Unknown scheme (${pathUri.scheme}) in $pathUri.'); - } - _sendExtensionImportResponse(sp, uri, libraryUri, null); - break; - } - break; - default: - _log('Unknown loader request tag=$tag from $isolateId'); - } -} diff --git a/runtime/bin/vmservice/vmservice_io.dart b/runtime/bin/vmservice/vmservice_io.dart index 4ed2f63b340..46fb27eebd8 100644 --- a/runtime/bin/vmservice/vmservice_io.dart +++ b/runtime/bin/vmservice/vmservice_io.dart @@ -14,7 +14,6 @@ import 'dart:isolate'; import 'dart:typed_data'; import 'dart:_vmservice'; -part 'loader.dart'; part 'server.dart'; // The TCP ip/port that the HTTP server listens on. @@ -58,7 +57,6 @@ _lazyServerBoot() { } Future cleanupCallback() async { - shutdownLoaders(); // Cancel the sigquit subscription. if (_signalSubscription != null) { await _signalSubscription.cancel(); @@ -264,11 +262,9 @@ main() { // scheduled microtasks. Timer.run(() {}); } - scriptLoadPort.handler = _processLoadRequest; // Register signal handler after a small delay to avoid stalling main // isolate startup. _registerSignalHandlerTimer = new Timer(shortDelay, _registerSignalHandler); - return scriptLoadPort; } _shutdown() native "VMServiceIO_Shutdown"; diff --git a/runtime/bin/vmservice/vmservice_sources.gni b/runtime/bin/vmservice/vmservice_sources.gni index 4c5d0c6d112..d326c2aec2e 100644 --- a/runtime/bin/vmservice/vmservice_sources.gni +++ b/runtime/bin/vmservice/vmservice_sources.gni @@ -5,7 +5,6 @@ # This file contains all Dart sources for the dart:io implementation of # the VM Service server. vmservice_sources = [ - "loader.dart", "server.dart", "vmservice_io.dart", ] diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h index cfc6cd975dc..aff0896aa25 100644 --- a/runtime/include/dart_api.h +++ b/runtime/include/dart_api.h @@ -3261,14 +3261,6 @@ DART_EXPORT void Dart_SetDartLibrarySourcesKernel( */ DART_EXPORT bool Dart_IsServiceIsolate(Dart_Isolate isolate); -/** - * Returns the port that script load requests should be sent on. - * - * \return Returns the port for load requests or ILLEGAL_PORT if the service - * isolate failed to startup or does not support load requests. - */ -DART_EXPORT Dart_Port Dart_ServiceWaitForLoadPort(); - /** * Writes the CPU profile to the timeline as a series of 'instant' events. * diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc index b578116853b..9df042e182a 100644 --- a/runtime/vm/dart_api_impl.cc +++ b/runtime/vm/dart_api_impl.cc @@ -5732,10 +5732,6 @@ DART_EXPORT bool Dart_IsServiceIsolate(Dart_Isolate isolate) { return ServiceIsolate::IsServiceIsolate(iso); } -DART_EXPORT Dart_Port Dart_ServiceWaitForLoadPort() { - return ServiceIsolate::WaitForLoadPort(); -} - DART_EXPORT int64_t Dart_TimelineGetMicros() { return OS::GetCurrentMonotonicMicros(); } diff --git a/runtime/vm/service_isolate.cc b/runtime/vm/service_isolate.cc index 2ddd7deb094..211b5948e57 100644 --- a/runtime/vm/service_isolate.cc +++ b/runtime/vm/service_isolate.cc @@ -78,7 +78,6 @@ Monitor* ServiceIsolate::monitor_ = new Monitor(); ServiceIsolate::State ServiceIsolate::state_ = ServiceIsolate::kStopped; Isolate* ServiceIsolate::isolate_ = NULL; Dart_Port ServiceIsolate::port_ = ILLEGAL_PORT; -Dart_Port ServiceIsolate::load_port_ = ILLEGAL_PORT; Dart_Port ServiceIsolate::origin_ = ILLEGAL_PORT; char* ServiceIsolate::server_address_ = NULL; char* ServiceIsolate::startup_failure_reason_ = nullptr; @@ -142,22 +141,11 @@ Dart_Port ServiceIsolate::Port() { return port_; } -Dart_Port ServiceIsolate::WaitForLoadPort() { - VMTagScope tagScope(Thread::Current(), VMTag::kLoadWaitTagId); - return WaitForLoadPortInternal(); -} - -Dart_Port ServiceIsolate::WaitForLoadPortInternal() { +void ServiceIsolate::WaitForServiceIsolateStartup() { MonitorLocker ml(monitor_); - while (state_ == kStarting && (load_port_ == ILLEGAL_PORT)) { + while (state_ == kStarting) { ml.Wait(); } - return load_port_; -} - -Dart_Port ServiceIsolate::LoadPort() { - MonitorLocker ml(monitor_); - return load_port_; } bool ServiceIsolate::SendServiceRpc(uint8_t* request_json, @@ -190,8 +178,7 @@ bool ServiceIsolate::SendServiceRpc(uint8_t* request_json, request.type = Dart_CObject_kArray; request.value.as_array.values = request_array; request.value.as_array.length = ARRAY_SIZE(request_array); - - ServiceIsolate::WaitForLoadPortInternal(); + ServiceIsolate::WaitForServiceIsolateStartup(); Dart_Port service_port = ServiceIsolate::Port(); const bool success = Dart_PostCObject(service_port, &request); @@ -303,11 +290,6 @@ void ServiceIsolate::SetServiceIsolate(Isolate* isolate) { } } -void ServiceIsolate::SetLoadPort(Dart_Port port) { - MonitorLocker ml(monitor_); - load_port_ = port; -} - void ServiceIsolate::MaybeMakeServiceIsolate(Isolate* I) { Thread* T = Thread::Current(); ASSERT(I == T->isolate()); @@ -452,7 +434,7 @@ class RunServiceTask : public ThreadPool::Task { ASSERT(I == T->isolate()); StackZone zone(T); HANDLESCOPE(T); - // Invoke main which will return the loadScriptPort. + // Invoke main which will set up the service port. const Library& root_library = Library::Handle(Z, I->object_store()->root_library()); if (root_library.IsNull()) { @@ -479,7 +461,6 @@ class RunServiceTask : public ThreadPool::Task { ASSERT(!entry.IsNull()); const Object& result = Object::Handle( Z, DartEntry::InvokeFunction(entry, Object::empty_array())); - ASSERT(!result.IsNull()); if (result.IsError()) { // Service isolate did not initialize properly. if (FLAG_trace_service) { @@ -493,9 +474,6 @@ class RunServiceTask : public ThreadPool::Task { } return false; } - ASSERT(result.IsReceivePort()); - const ReceivePort& rp = ReceivePort::Cast(result); - ServiceIsolate::SetLoadPort(rp.Id()); return false; } }; diff --git a/runtime/vm/service_isolate.h b/runtime/vm/service_isolate.h index a3c1fdfd11c..8940f6ac010 100644 --- a/runtime/vm/service_isolate.h +++ b/runtime/vm/service_isolate.h @@ -26,9 +26,7 @@ class ServiceIsolate : public AllStatic { static bool IsServiceIsolate(const Isolate* isolate); static bool IsServiceIsolateDescendant(const Isolate* isolate); static Dart_Port Port(); - - static Dart_Port WaitForLoadPort(); - static Dart_Port LoadPort(); + static void WaitForServiceIsolateStartup(); // Returns `true` if the request was sucessfully sent. If it was, the // [reply_port] will receive a Dart_TypedData_kUint8 response json. @@ -62,13 +60,9 @@ class ServiceIsolate : public AllStatic { private: static void KillServiceIsolate(); - // Does not need a current thread. - static Dart_Port WaitForLoadPortInternal(); - protected: static void SetServicePort(Dart_Port port); static void SetServiceIsolate(Isolate* isolate); - static void SetLoadPort(Dart_Port port); static void FinishedExiting(); static void FinishedInitializing(); static void InitializingFailed(char* error); @@ -88,7 +82,6 @@ class ServiceIsolate : public AllStatic { static State state_; static Isolate* isolate_; static Dart_Port port_; - static Dart_Port load_port_; static Dart_Port origin_; static char* server_address_; diff --git a/sdk/lib/_internal/vm/bin/builtin.dart b/sdk/lib/_internal/vm/bin/builtin.dart index 6977c89af53..b79755ef7ec 100644 --- a/sdk/lib/_internal/vm/bin/builtin.dart +++ b/sdk/lib/_internal/vm/bin/builtin.dart @@ -10,6 +10,7 @@ library builtin; import 'dart:async'; import 'dart:collection' hide LinkedList, LinkedListEntry; import 'dart:_internal' hide Symbol; +import 'dart:io'; import 'dart:isolate'; import 'dart:typed_data'; @@ -33,48 +34,30 @@ void _print(arg) { @pragma("vm:entry-point") _getPrintClosure() => _print; -// Asynchronous loading of resources. -// The embedder forwards loading requests to the service isolate. - -// A port for communicating with the service isolate for I/O. -@pragma("vm:entry-point") -SendPort _loadPort; - -// The isolateId used to communicate with the service isolate for I/O. -@pragma("vm:entry-point") -int _isolateId; - -// Requests made to the service isolate over the load port. - -// Extra requests. Keep these in sync between loader.dart and builtin.dart. -const _Dart_kGetPackageConfigUri = 7; // Uri of the .packages file. -const _Dart_kResolvePackageUri = 8; // Resolve a package: uri. - -// Make a request to the loader. Future will complete with result which is -// either a Uri or a List. -Future _makeLoaderRequest(int tag, String uri) { - assert(_isolateId != null); - if (_loadPort == null) { - throw new UnsupportedError("Service isolate is not available."); - } - Completer completer = new Completer(); - RawReceivePort port = new RawReceivePort(); - port.handler = (msg) { - // Close the port. - port.close(); - completer.complete(msg); - }; - _loadPort.send([_traceLoading, _isolateId, tag, port.sendPort, uri]); - return completer.future; -} - // The current working directory when the embedder was launched. Uri _workingDirectory; + // The URI that the root script was loaded from. Remembered so that // package imports can be resolved relative to it. The root script is the basis // for the root library in the VM. Uri _rootScript; +// packagesConfig specified for the isolate. +Uri _packagesConfigUri; + +// Packages are either resolved looking up in a map or resolved from within a +// package root. +bool get _packagesReady => (_packageMap != null) || (_packageError != null); + +// Error string set if there was an error resolving package configuration. +// For example not finding a .packages file or packages/ directory, malformed +// .packages file or any other related error. +String _packageError = null; + +// The map describing how certain package names are mapped to Uris. +Uri _packageConfig = null; +Map _packageMap = null; + // Special handling for Windows paths so that they are compatible with URI // handling. // Embedder sets this to true if we are running on Windows. @@ -111,29 +94,473 @@ _sanitizeWindowsPath(path) { return fixedPath; } -_trimWindowsPath(path) { - // Convert /X:/ to X:/. - if (_isWindows == false) { - // Do nothing when not running Windows. - return path; +_setPackagesConfig(String packagesParam) { + var packagesName = _sanitizeWindowsPath(packagesParam); + var packagesUri = Uri.parse(packagesName); + if (packagesUri.scheme == '') { + // Script does not have a scheme, assume that it is a path, + // resolve it against the working directory. + packagesUri = _workingDirectory.resolveUri(packagesUri); } - if (!path.startsWith('/') || (path.length < 3)) { - return path; - } - // Match '/?:'. - if ((path[0] == '/') && (path[2] == ':')) { - // Remove leading '/'. - return path.substring(1); - } - return path; + _packagesConfigUri = packagesUri; } -// Ensure we have a trailing slash character. -_enforceTrailingSlash(uri) { - if (!uri.endsWith('/')) { - return '$uri/'; +// Given a uri with a 'package' scheme, return a Uri that is prefixed with +// the package root or resolved relative to the package configuration. +Uri _resolvePackageUri(Uri uri) { + assert(uri.scheme == "package"); + assert(_packagesReady); + + if (uri.host.isNotEmpty) { + var path = '${uri.host}${uri.path}'; + var right = 'package:$path'; + var wrong = 'package://$path'; + + throw "URIs using the 'package:' scheme should look like " + "'$right', not '$wrong'."; + } + + var packageNameEnd = uri.path.indexOf('/'); + if (packageNameEnd == 0) { + // Package URIs must have a non-empty package name (not start with "/"). + throw "URIS using the 'package:' scheme should look like " + "'package:packageName${uri.path}', not 'package:${uri.path}'"; + } + if (_traceLoading) { + _log('Resolving package with uri path: ${uri.path}'); + } + var resolvedUri; + if (_packageError != null) { + if (_traceLoading) { + _log("Resolving package with pending resolution error: $_packageError"); + } + throw _packageError; + } else { + if (packageNameEnd < 0) { + // Package URIs must have a path after the package name, even if it's + // just "/". + throw "URIS using the 'package:' scheme should look like " + "'package:${uri.path}/', not 'package:${uri.path}'"; + } + var packageName = uri.path.substring(0, packageNameEnd); + var mapping = _packageMap[packageName]; + if (_traceLoading) { + _log("Mapped '$packageName' package to '$mapping'"); + } + if (mapping == null) { + throw "No mapping for '$packageName' package when resolving '$uri'."; + } + var path; + assert(uri.path.length > packageName.length); + path = uri.path.substring(packageName.length + 1); + if (_traceLoading) { + _log("Path to be resolved in package: $path"); + } + resolvedUri = mapping.resolve(path); + } + if (_traceLoading) { + _log("Resolved '$uri' to '$resolvedUri'."); + } + return resolvedUri; +} + +void _requestPackagesMap(Uri packageConfig) { + dynamic msg = null; + if (packageConfig != null) { + // Explicitly specified .packages path. + msg = _handlePackagesRequest(_traceLoading, -2, packageConfig); + } else { + // Search for .packages starting at the root script. + msg = _handlePackagesRequest(_traceLoading, -1, _rootScript); + } + if (_traceLoading) { + _log("Requested packages map for '$_rootScript'."); + } + if (msg is String) { + if (_traceLoading) { + _log("Got failure response on package port: '$msg'"); + } + // Remember the error message. + _packageError = msg; + } else if (msg is List) { + // First entry contains the location of the loaded .packages file. + assert((msg.length % 2) == 0); + assert(msg.length >= 2); + assert(msg[1] == null); + _packageConfig = Uri.parse(msg[0]); + _packageMap = new Map(); + for (var i = 2; i < msg.length; i += 2) { + // TODO(iposva): Complain about duplicate entries. + _packageMap[msg[i]] = Uri.parse(msg[i + 1]); + } + if (_traceLoading) { + _log("Setup package map: $_packageMap"); + } + } else { + _packageError = "Bad type of packages reply: ${msg.runtimeType}"; + if (_traceLoading) { + _log(_packageError); + } + } +} + +// Handling of packages requests. Finding and parsing of .packages file or +// packages/ directories. +const _LF = 0x0A; +const _CR = 0x0D; +const _SPACE = 0x20; +const _HASH = 0x23; +const _DOT = 0x2E; +const _COLON = 0x3A; +const _DEL = 0x7F; + +const _invalidPackageNameChars = const [ + true, // space + false, // ! + true, // " + true, // # + false, // $ + true, // % + false, // & + false, // ' + false, // ( + false, // ) + false, // * + false, // + + false, // , + false, // - + false, // . + true, // / + false, // 0 + false, // 1 + false, // 2 + false, // 3 + false, // 4 + false, // 5 + false, // 6 + false, // 7 + false, // 8 + false, // 9 + true, // : + false, // ; + true, // < + false, // = + true, // > + true, // ? + false, // @ + false, // A + false, // B + false, // C + false, // D + false, // E + false, // F + false, // G + false, // H + false, // I + false, // J + false, // K + false, // L + false, // M + false, // N + false, // O + false, // P + false, // Q + false, // R + false, // S + false, // T + false, // U + false, // V + false, // W + false, // X + false, // Y + false, // Z + true, // [ + true, // \ + true, // ] + true, // ^ + false, // _ + true, // ` + false, // a + false, // b + false, // c + false, // d + false, // e + false, // f + false, // g + false, // h + false, // i + false, // j + false, // k + false, // l + false, // m + false, // n + false, // o + false, // p + false, // q + false, // r + false, // s + false, // t + false, // u + false, // v + false, // w + false, // x + false, // y + false, // z + true, // { + true, // | + true, // } + false, // ~ + true, // DEL +]; + +_parsePackagesFile(bool traceLoading, Uri packagesFile, List data) { + // The first entry contains the location of the identified .packages file + // instead of a mapping. + var result = [packagesFile.toString(), null]; + var index = 0; + var len = data.length; + while (index < len) { + var start = index; + var char = data[index]; + if ((char == _CR) || (char == _LF)) { + // Skipping empty lines. + index++; + continue; + } + + // Identify split within the line and end of the line. + var separator = -1; + var end = len; + // Verifying validity of package name while scanning the line. + var nonDot = false; + var invalidPackageName = false; + + // Scan to the end of the line or data. + while (index < len) { + char = data[index++]; + // If we have not reached the separator yet, determine whether we are + // scanning legal package name characters. + if (separator == -1) { + if ((char == _COLON)) { + // The first colon on a line is the separator between package name and + // related URI. + separator = index - 1; + } else { + // Still scanning the package name part. Check for the validity of + // the characters. + nonDot = nonDot || (char != _DOT); + invalidPackageName = invalidPackageName || + (char < _SPACE) || + (char > _DEL) || + _invalidPackageNameChars[char - _SPACE]; + } + } + // Identify end of line. + if ((char == _CR) || (char == _LF)) { + end = index - 1; + break; + } + } + + // No further handling needed for comment lines. + if (data[start] == _HASH) { + if (traceLoading) { + _log("Skipping comment in $packagesFile:\n" + "${new String.fromCharCodes(data, start, end)}"); + } + continue; + } + + // Check for a badly formatted line, starting with a ':'. + if (separator == start) { + var line = new String.fromCharCodes(data, start, end); + if (traceLoading) { + _log("Line starts with ':' in $packagesFile:\n" + "$line"); + } + return "Missing package name in $packagesFile:\n" + "$line"; + } + + // Ensure there is a separator on the line. + if (separator == -1) { + var line = new String.fromCharCodes(data, start, end); + if (traceLoading) { + _log("Line has no ':' in $packagesFile:\n" + "$line"); + } + return "Missing ':' separator in $packagesFile:\n" + "$line"; + } + + var packageName = new String.fromCharCodes(data, start, separator); + + // Check for valid package name. + if (invalidPackageName || !nonDot) { + var line = new String.fromCharCodes(data, start, end); + if (traceLoading) { + _log("Invalid package name $packageName in $packagesFile"); + } + return "Invalid package name '$packageName' in $packagesFile:\n" + "$line"; + } + + if (traceLoading) { + _log("packageName: $packageName"); + } + var packageUri = new String.fromCharCodes(data, separator + 1, end); + if (traceLoading) { + _log("original packageUri: $packageUri"); + } + // Ensure the package uri ends with a /. + if (!packageUri.endsWith("/")) { + packageUri = "$packageUri/"; + } + packageUri = packagesFile.resolve(packageUri).toString(); + if (traceLoading) { + _log("mapping: $packageName -> $packageUri"); + } + result.add(packageName); + result.add(packageUri); + } + + if (traceLoading) { + _log("Parsed packages file at $packagesFile. Sending:\n$result"); + } + return result; +} + +_loadPackagesFile(bool traceLoading, Uri packagesFile) { + try { + var data = new File.fromUri(packagesFile).readAsBytesSync(); + if (traceLoading) { + _log("Loaded packages file from $packagesFile:\n" + "${new String.fromCharCodes(data)}"); + } + return _parsePackagesFile(traceLoading, packagesFile, data); + } catch (e, s) { + if (traceLoading) { + _log("Error loading packages: $e\n$s"); + } + return "Uncaught error ($e) loading packages file."; + } +} + +_findPackagesFile(bool traceLoading, Uri base) { + try { + // Walk up the directory hierarchy to check for the existence of + // .packages files in parent directories and for the existence of a + // packages/ directory on the first iteration. + var dir = new File.fromUri(base).parent; + var prev = null; + // Keep searching until we reach the root. + while ((prev == null) || (prev.path != dir.path)) { + // Check for the existence of a .packages file and if it exists try to + // load and parse it. + var dirUri = dir.uri; + var packagesFile = dirUri.resolve(".packages"); + if (traceLoading) { + _log("Checking for $packagesFile file."); + } + var exists = new File.fromUri(packagesFile).existsSync(); + if (traceLoading) { + _log("$packagesFile exists: $exists"); + } + if (exists) { + return _loadPackagesFile(traceLoading, packagesFile); + } + // Move up one level. + prev = dir; + dir = dir.parent; + } + + // No .packages file was found. + if (traceLoading) { + _log("Could not resolve a package location from $base"); + } + return "Could not resolve a package location for base at $base"; + } catch (e, s) { + if (traceLoading) { + _log("Error loading packages: $e\n$s"); + } + return "Uncaught error ($e) loading packages file."; + } +} + +_loadPackagesData(traceLoading, resource) { + try { + var data = resource.data; + var mime = data.mimeType; + if (mime != "text/plain") { + throw "MIME-type must be text/plain: $mime given."; + } + var charset = data.charset; + if ((charset != "utf-8") && (charset != "US-ASCII")) { + // The C++ portion of the embedder assumes UTF-8. + throw "Only utf-8 or US-ASCII encodings are supported: $charset given."; + } + return _parsePackagesFile(traceLoading, resource, data.contentAsBytes()); + } catch (e) { + return "Uncaught error ($e) loading packages data."; + } +} + +_handlePackagesRequest(bool traceLoading, int tag, Uri resource) { + try { + if (tag == -1) { + if (resource.scheme == '' || resource.scheme == 'file') { + return _findPackagesFile(traceLoading, resource); + } else { + return "Unsupported scheme used to locate .packages file:'$resource'."; + } + } else if (tag == -2) { + if (traceLoading) { + _log("Handling load of packages map: '$resource'."); + } + if (resource.scheme == '' || resource.scheme == 'file') { + var exists = new File.fromUri(resource).existsSync(); + if (exists) { + return _loadPackagesFile(traceLoading, resource); + } else { + return "Packages file '$resource' not found."; + } + } else if (resource.scheme == 'data') { + return _loadPackagesData(traceLoading, resource); + } else { + return "Unknown scheme (${resource.scheme}) for package file at " + "'$resource'."; + } + } else { + return "Unknown packages request tag: $tag for '$resource'."; + } + } catch (e, s) { + if (traceLoading) { + _log("Error handling packages request: $e\n$s"); + } + return "Uncaught error ($e) handling packages request."; + } +} + +// Embedder Entrypoint: +// The embedder calls this method to initial the package resolution state. +@pragma("vm:entry-point") +void _Init(String packagesConfig, String workingDirectory, String rootScript) { + // Register callbacks and hooks with the rest of core libraries. + _setupHooks(); + + // _workingDirectory must be set first. + _workingDirectory = new Uri.directory(workingDirectory); + + // setup _rootScript. + if (rootScript != null) { + _rootScript = Uri.parse(rootScript); + } + + // If the --packages flag was passed, setup _packagesConfig. + if (packagesConfig != null) { + _packageMap = null; + _setPackagesConfig(packagesConfig); } - return uri; } // Embedder Entrypoint: @@ -224,11 +651,14 @@ Future _getPackageConfigFuture() { if (_traceLoading) { _log("Request for package config from user code."); } - assert(_loadPort != null); - return _makeLoaderRequest(_Dart_kGetPackageConfigUri, null); + if (!_packagesReady) { + _requestPackagesMap(_packagesConfigUri); + } + // Respond with the packages config (if any) after package resolution. + return Future.value(_packageConfig); } -Future _resolvePackageUriFuture(Uri packageUri) async { +Future _resolvePackageUriFuture(Uri packageUri) { if (_traceLoading) { _log("Request for package Uri resolution from user code: $packageUri"); } @@ -237,18 +667,22 @@ Future _resolvePackageUriFuture(Uri packageUri) async { _log("Non-package Uri, returning unmodified: $packageUri"); } // Return the incoming parameter if not passed a package: URI. - return packageUri; + return Future.value(packageUri); } - var result = await _makeLoaderRequest( - _Dart_kResolvePackageUri, packageUri.toString()); - if (result is! Uri) { + if (!_packagesReady) { + _requestPackagesMap(_packagesConfigUri); + } + Uri resolvedUri; + try { + resolvedUri = _resolvePackageUri(packageUri); + } catch (e, s) { if (_traceLoading) { _log("Exception when resolving package URI: $packageUri"); } - result = null; + resolvedUri = null; } if (_traceLoading) { - _log("Resolved '$packageUri' to '$result'"); + _log("Resolved '$packageUri' to '$resolvedUri'"); } - return result; + return Future.value(resolvedUri); } diff --git a/sdk_nnbd/lib/_internal/vm/bin/builtin.dart b/sdk_nnbd/lib/_internal/vm/bin/builtin.dart index a569688197d..f202b3f6187 100644 --- a/sdk_nnbd/lib/_internal/vm/bin/builtin.dart +++ b/sdk_nnbd/lib/_internal/vm/bin/builtin.dart @@ -10,6 +10,7 @@ library builtin; import 'dart:async'; import 'dart:collection' hide LinkedList, LinkedListEntry; import 'dart:_internal' hide Symbol; +import 'dart:io'; import 'dart:isolate'; import 'dart:typed_data'; @@ -33,48 +34,30 @@ void _print(arg) { @pragma("vm:entry-point") _getPrintClosure() => _print; -// Asynchronous loading of resources. -// The embedder forwards loading requests to the service isolate. - -// A port for communicating with the service isolate for I/O. -@pragma("vm:entry-point") -SendPort _loadPort; - -// The isolateId used to communicate with the service isolate for I/O. -@pragma("vm:entry-point") -int _isolateId; - -// Requests made to the service isolate over the load port. - -// Extra requests. Keep these in sync between loader.dart and builtin.dart. -const _Dart_kGetPackageConfigUri = 7; // Uri of the .packages file. -const _Dart_kResolvePackageUri = 8; // Resolve a package: uri. - -// Make a request to the loader. Future will complete with result which is -// either a Uri or a List. -Future _makeLoaderRequest(int tag, String uri) { - assert(_isolateId != null); - if (_loadPort == null) { - throw new UnsupportedError("Service isolate is not available."); - } - Completer completer = new Completer(); - RawReceivePort port = new RawReceivePort(); - port.handler = (msg) { - // Close the port. - port.close(); - completer.complete(msg); - }; - _loadPort.send([_traceLoading, _isolateId, tag, port.sendPort, uri]); - return completer.future; -} - // The current working directory when the embedder was launched. Uri _workingDirectory; + // The URI that the root script was loaded from. Remembered so that // package imports can be resolved relative to it. The root script is the basis // for the root library in the VM. Uri _rootScript; +// packagesConfig specified for the isolate. +Uri _packagesConfigUri; + +// Packages are either resolved looking up in a map or resolved from within a +// package root. +bool get _packagesReady => (_packageMap != null) || (_packageError != null); + +// Error string set if there was an error resolving package configuration. +// For example not finding a .packages file or packages/ directory, malformed +// .packages file or any other related error. +String _packageError = null; + +// The map describing how certain package names are mapped to Uris. +Uri _packageConfig = null; +Map _packageMap = null; + // Special handling for Windows paths so that they are compatible with URI // handling. // Embedder sets this to true if we are running on Windows. @@ -111,29 +94,474 @@ _sanitizeWindowsPath(path) { return fixedPath; } -_trimWindowsPath(path) { - // Convert /X:/ to X:/. - if (_isWindows == false) { - // Do nothing when not running Windows. - return path; +_setPackagesConfig(String packagesParam) { + var packagesName = _sanitizeWindowsPath(packagesParam); + var packagesUri = Uri.parse(packagesName); + if (packagesUri.scheme == '') { + // Script does not have a scheme, assume that it is a path, + // resolve it against the working directory. + packagesUri = _workingDirectory.resolveUri(packagesUri); } - if (!path.startsWith('/') || (path.length < 3)) { - return path; - } - // Match '/?:'. - if ((path[0] == '/') && (path[2] == ':')) { - // Remove leading '/'. - return path.substring(1); - } - return path; + _packagesConfigUri = packagesUri; } -// Ensure we have a trailing slash character. -_enforceTrailingSlash(uri) { - if (!uri.endsWith('/')) { - return '$uri/'; +// Given a uri with a 'package' scheme, return a Uri that is prefixed with +// the package root or resolved relative to the package configuration. +Uri _resolvePackageUri(Uri uri) { + assert(uri.scheme == "package"); + assert(_packagesReady); + + if (uri.host.isNotEmpty) { + var path = '${uri.host}${uri.path}'; + var right = 'package:$path'; + var wrong = 'package://$path'; + + throw "URIs using the 'package:' scheme should look like " + "'$right', not '$wrong'."; + } + + var packageNameEnd = uri.path.indexOf('/'); + if (packageNameEnd == 0) { + // Package URIs must have a non-empty package name (not start with "/"). + throw "URIS using the 'package:' scheme should look like " + "'package:packageName${uri.path}', not 'package:${uri.path}'"; + } + if (_traceLoading) { + _log('Resolving package with uri path: ${uri.path}'); + } + var resolvedUri; + if (_packageError != null) { + if (_traceLoading) { + _log("Resolving package with pending resolution error: $_packageError"); + } + throw _packageError; + } else { + if (packageNameEnd < 0) { + // Package URIs must have a path after the package name, even if it's + // just "/". + throw "URIS using the 'package:' scheme should look like " + "'package:${uri.path}/', not 'package:${uri.path}'"; + } + var packageName = uri.path.substring(0, packageNameEnd); + var mapping = _packageMap[packageName]; + if (_traceLoading) { + _log("Mapped '$packageName' package to '$mapping'"); + } + if (mapping == null) { + throw "No mapping for '$packageName' package when resolving '$uri'."; + } + var path; + assert(uri.path.length > packageName.length); + path = uri.path.substring(packageName.length + 1); + if (_traceLoading) { + _log("Path to be resolved in package: $path"); + } + resolvedUri = mapping.resolve(path); + } + if (_traceLoading) { + _log("Resolved '$uri' to '$resolvedUri'."); + } + return resolvedUri; +} + +void _requestPackagesMap(Uri packageConfig) { + var msg = null; + if (packageConfig != null) { + // Explicitly specified .packages path. + msg = _handlePackagesRequest(_traceLoading, -2, packageConfig); + } else { + // Search for .packages starting at the root script. + msg = _handlePackagesRequest(_traceLoading, -1, _rootScript); + } + if (_traceLoading) { + _log("Requested packages map for '$_rootScript'."); + } + if (msg is String) { + if (_traceLoading) { + _log("Got failure response on package port: '$msg'"); + } + // Remember the error message. + _packageError = msg; + } else if (msg is List) { + // First entry contains the location of the loaded .packages file. + assert((msg.length % 2) == 0); + assert(msg.length >= 2); + assert(msg[1] == null); + _packageConfig = Uri.parse(msg[0]); + _packageMap = new Map(); + for (var i = 2; i < msg.length; i += 2) { + // TODO(iposva): Complain about duplicate entries. + _packageMap[msg[i]] = Uri.parse(msg[i + 1]); + } + if (_traceLoading) { + _log("Setup package map: $_packageMap"); + } + } else { + _packageError = "Bad type of packages reply: ${msg.runtimeType}"; + if (_traceLoading) { + _log(_packageError); + } + } +} + +// Handling of packages requests. Finding and parsing of .packages file or +// packages/ directories. +const _LF = 0x0A; +const _CR = 0x0D; +const _SPACE = 0x20; +const _HASH = 0x23; +const _DOT = 0x2E; +const _COLON = 0x3A; +const _DEL = 0x7F; + +const _invalidPackageNameChars = const [ + true, // space + false, // ! + true, // " + true, // # + false, // $ + true, // % + false, // & + false, // ' + false, // ( + false, // ) + false, // * + false, // + + false, // , + false, // - + false, // . + true, // / + false, // 0 + false, // 1 + false, // 2 + false, // 3 + false, // 4 + false, // 5 + false, // 6 + false, // 7 + false, // 8 + false, // 9 + true, // : + false, // ; + true, // < + false, // = + true, // > + true, // ? + false, // @ + false, // A + false, // B + false, // C + false, // D + false, // E + false, // F + false, // G + false, // H + false, // I + false, // J + false, // K + false, // L + false, // M + false, // N + false, // O + false, // P + false, // Q + false, // R + false, // S + false, // T + false, // U + false, // V + false, // W + false, // X + false, // Y + false, // Z + true, // [ + true, // \ + true, // ] + true, // ^ + false, // _ + true, // ` + false, // a + false, // b + false, // c + false, // d + false, // e + false, // f + false, // g + false, // h + false, // i + false, // j + false, // k + false, // l + false, // m + false, // n + false, // o + false, // p + false, // q + false, // r + false, // s + false, // t + false, // u + false, // v + false, // w + false, // x + false, // y + false, // z + true, // { + true, // | + true, // } + false, // ~ + true, // DEL +]; + +_parsePackagesFile(bool traceLoading, Uri packagesFile, List data) { + // The first entry contains the location of the identified .packages file + // instead of a mapping. + var result = [packagesFile.toString(), null]; + var index = 0; + var len = data.length; + while (index < len) { + var start = index; + var char = data[index]; + if ((char == _CR) || (char == _LF)) { + // Skipping empty lines. + index++; + continue; + } + + // Identify split within the line and end of the line. + var separator = -1; + var end = len; + // Verifying validity of package name while scanning the line. + var nonDot = false; + var invalidPackageName = false; + + // Scan to the end of the line or data. + while (index < len) { + char = data[index++]; + // If we have not reached the separator yet, determine whether we are + // scanning legal package name characters. + if (separator == -1) { + if ((char == _COLON)) { + // The first colon on a line is the separator between package name and + // related URI. + separator = index - 1; + } else { + // Still scanning the package name part. Check for the validity of + // the characters. + nonDot = nonDot || (char != _DOT); + invalidPackageName = invalidPackageName || + (char < _SPACE) || + (char > _DEL) || + _invalidPackageNameChars[char - _SPACE]; + } + } + // Identify end of line. + if ((char == _CR) || (char == _LF)) { + end = index - 1; + break; + } + } + + // No further handling needed for comment lines. + if (data[start] == _HASH) { + if (traceLoading) { + _log("Skipping comment in $packagesFile:\n" + "${new String.fromCharCodes(data, start, end)}"); + } + continue; + } + + // Check for a badly formatted line, starting with a ':'. + if (separator == start) { + var line = new String.fromCharCodes(data, start, end); + if (traceLoading) { + _log("Line starts with ':' in $packagesFile:\n" + "$line"); + } + return "Missing package name in $packagesFile:\n" + "$line"; + } + + // Ensure there is a separator on the line. + if (separator == -1) { + var line = new String.fromCharCodes(data, start, end); + if (traceLoading) { + _log("Line has no ':' in $packagesFile:\n" + "$line"); + } + return "Missing ':' separator in $packagesFile:\n" + "$line"; + } + + var packageName = new String.fromCharCodes(data, start, separator); + + // Check for valid package name. + if (invalidPackageName || !nonDot) { + var line = new String.fromCharCodes(data, start, end); + if (traceLoading) { + _log("Invalid package name $packageName in $packagesFile"); + } + return "Invalid package name '$packageName' in $packagesFile:\n" + "$line"; + } + + if (traceLoading) { + _log("packageName: $packageName"); + } + var packageUri = new String.fromCharCodes(data, separator + 1, end); + if (traceLoading) { + _log("original packageUri: $packageUri"); + } + // Ensure the package uri ends with a /. + if (!packageUri.endsWith("/")) { + packageUri = "$packageUri/"; + } + packageUri = packagesFile.resolve(packageUri).toString(); + if (traceLoading) { + _log("mapping: $packageName -> $packageUri"); + } + result.add(packageName); + result.add(packageUri); + } + + if (traceLoading) { + _log("Parsed packages file at $packagesFile. Sending:\n$result"); + } + return result; +} + +_loadPackagesFile(bool traceLoading, Uri packagesFile) { + try { + var data = new File.fromUri(packagesFile).readAsBytesSync(); + if (traceLoading) { + _log("Loaded packages file from $packagesFile:\n" + "${new String.fromCharCodes(data)}"); + } + return _parsePackagesFile(traceLoading, packagesFile, data); + } catch (e, s) { + if (traceLoading) { + _log("Error loading packages: $e\n$s"); + } + return "Uncaught error ($e) loading packages file."; + } +} + +_findPackagesFile(bool traceLoading, Uri base) { + try { + // Walk up the directory hierarchy to check for the existence of + // .packages files in parent directories and for the existence of a + // packages/ directory on the first iteration. + var dir = new File.fromUri(base).parent; + var prev = null; + // Keep searching until we reach the root. + while ((prev == null) || (prev.path != dir.path)) { + // Check for the existence of a .packages file and if it exists try to + // load and parse it. + var dirUri = dir.uri; + var packagesFile = dirUri.resolve(".packages"); + if (traceLoading) { + _log("Checking for $packagesFile file."); + } + var exists = new File.fromUri(packagesFile).existsSync(); + if (traceLoading) { + _log("$packagesFile exists: $exists"); + } + if (exists) { + _loadPackagesFile(traceLoading, packagesFile); + return; + } + // Move up one level. + prev = dir; + dir = dir.parent; + } + + // No .packages file was found. + if (traceLoading) { + _log("Could not resolve a package location from $base"); + } + return "Could not resolve a package location for base at $base"; + } catch (e, s) { + if (traceLoading) { + _log("Error loading packages: $e\n$s"); + } + return "Uncaught error ($e) loading packages file."; + } +} + +_loadPackagesData(traceLoading, resource) { + try { + var data = resource.data; + var mime = data.mimeType; + if (mime != "text/plain") { + throw "MIME-type must be text/plain: $mime given."; + } + var charset = data.charset; + if ((charset != "utf-8") && (charset != "US-ASCII")) { + // The C++ portion of the embedder assumes UTF-8. + throw "Only utf-8 or US-ASCII encodings are supported: $charset given."; + } + return _parsePackagesFile(traceLoading, resource, data.contentAsBytes()); + } catch (e) { + return "Uncaught error ($e) loading packages data."; + } +} + +_handlePackagesRequest(bool traceLoading, int tag, Uri resource) { + try { + if (tag == -1) { + if (resource.scheme == '' || resource.scheme == 'file') { + return _findPackagesFile(traceLoading, resource); + } else { + return "Unsupported scheme used to locate .packages file:'$resource'."; + } + } else if (tag == -2) { + if (traceLoading) { + _log("Handling load of packages map: '$resource'."); + } + if (resource.scheme == '' || resource.scheme == 'file') { + var exists = new File.fromUri(resource).existsSync(); + if (exists) { + return _loadPackagesFile(traceLoading, resource); + } else { + return "Packages file '$resource' not found."; + } + } else if (resource.scheme == 'data') { + return _loadPackagesData(traceLoading, resource); + } else { + return "Unknown scheme (${resource.scheme}) for package file at " + "'$resource'."; + } + } else { + return "Unknown packages request tag: $tag for '$resource'."; + } + } catch (e, s) { + if (traceLoading) { + _log("Error handling packages request: $e\n$s"); + } + return "Uncaught error ($e) handling packages request."; + } +} + +// Embedder Entrypoint: +// The embedder calls this method to initial the package resolution state. +@pragma("vm:entry-point") +void _Init(String packagesConfig, String workingDirectory, String rootScript) { + // Register callbacks and hooks with the rest of core libraries. + _setupHooks(); + + // _workingDirectory must be set first. + _workingDirectory = new Uri.directory(workingDirectory); + + // setup _rootScript. + if (rootScript != null) { + _rootScript = Uri.parse(rootScript); + } + + // If the --packages flag was passed, setup _packagesConfig. + if (packagesConfig != null) { + _packageMap = null; + _setPackagesConfig(packagesConfig); } - return uri; } // Embedder Entrypoint: @@ -224,11 +652,14 @@ Future _getPackageConfigFuture() { if (_traceLoading) { _log("Request for package config from user code."); } - assert(_loadPort != null); - return _makeLoaderRequest(_Dart_kGetPackageConfigUri, null); + if (!_packagesReady) { + _requestPackagesMap(_packagesConfigUri); + } + // Respond with the packages config (if any) after package resolution. + return Future.value(_packageConfig); } -Future _resolvePackageUriFuture(Uri packageUri) async { +Future _resolvePackageUriFuture(Uri packageUri) { if (_traceLoading) { _log("Request for package Uri resolution from user code: $packageUri"); } @@ -237,18 +668,22 @@ Future _resolvePackageUriFuture(Uri packageUri) async { _log("Non-package Uri, returning unmodified: $packageUri"); } // Return the incoming parameter if not passed a package: URI. - return packageUri; + return Future.value(packageUri); } - var result = await _makeLoaderRequest( - _Dart_kResolvePackageUri, packageUri.toString()); - if (result is! Uri) { + if (!_packagesReady) { + _requestPackagesMap(_packagesConfigUri); + } + Uri resolvedUri; + try { + resolvedUri = _resolvePackageUri(packageUri); + } catch (e, s) { if (_traceLoading) { _log("Exception when resolving package URI: $packageUri"); } - result = null; + resolvedUri = null; } if (_traceLoading) { - _log("Resolved '$packageUri' to '$result'"); + _log("Resolved '$packageUri' to '$resolvedUri'"); } - return result; + return Future.value(resolvedUri); }