dart-sdk/runtime/bin/loader.cc
Alexander Aprelev 0425997b31 Second attempt to reland "[vm/concurrency] Introduce concept of Isolate Groups"
This reverts commit 3d14b75f97.

An Isolate Group (IG) is a collection of isolates which were spawned from the
same source. This allows the VM to:

  * have a guarantee that all isolates within one IG can safely exchange
    structured objects (currently we rely on embedder for this
    guarantee)

  * hot-reload all isolates together (currently we only reload one
    isolate, leaving same-source isolates in inconsistent state)

  * make a shared heap for all isolates from the same IG, which paves
    the way for faster communication and sharing of immutable objects.

All isolates within one IG will share the same IsolateGroupSource.

**Embedder changes**

This change makes breaking embedder API changes to support this new
concept of Isolate Groups: The existing isolate lifecycle callbacks
given to Dart_Initialize will become Isolate Group lifecycle callbacks.
A new callback `initialize_isolate` callback will be added which can
initialize a new isolate within an existing IG.

Existing embedders can be updated by performing the following renames

  Dart_CreateIsolate -> Dart_CreateIsolateGroup
  Dart_IsolateCreateCallback -> Dart_IsolateGroupCreateCallback
  Dart_IsolateCleanupCallback -> Dart_IsolateGroupShutdownCallback
  Dart_CreateIsolateFromKernel -> Dart_CreateIsolateGroupFromKernel
  Dart_CurrentIsolateData -> Dart_CurrentIsolateGroupData
  Dart_IsolateData -> Dart_IsolateGroupData
  Dart_GetNativeIsolateData -> Dart_GetNativeIsolateGroupData
  Dart_InitializeParams.create -> Dart_InitializeParams.create_group
  Dart_InitializeParams.cleanup -> Dart_InitializeParams.shutdown_group
  Dart_InitializeParams.shutdown -> Dart_InitializeParams.shutdown_isolate

By default `Isolate.spawn` will cause the creation of a new IG.

Though an embedder can opt-into supporting multiple isolates within one IG by
providing a callback to the newly added `Dart_InitializeParams.initialize_isolate`.
The responsibility of this new callback is to initialize an existing
isolate (which was setup by re-using source code from the spawning
isolate - i.e. the one which used `Isolate.spawn`) by setting native
resolvers, initializing global state, etc.

Issue https://github.com/dart-lang/sdk/issues/36648
Issue https://github.com/dart-lang/sdk/issues/36097

Original review: https://dart-review.googlesource.com/c/sdk/+/105241

Difference to original review:

  * Give each isolate it's own [Loader] (for now)
  * Sort classes during initialization for spawned isolates if app-jit is used (to match main isolate)
  * Fix IsolateData memory leak if isolate startup fails

Difference to first reland(Patchset 2):

  * Fix typo where memory was freed twice.

Change-Id: Ib1c83fe83b629cd50ae6af90ee99fdd44da882d4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/108367
Commit-Queue: Alexander Aprelev <aam@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
2019-07-08 23:49:05 +00:00

898 lines
31 KiB
C++

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