mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Snapshots compiled with sound null-safety enabled require changes to the way in which isolates are launched. Specifically, the `Dart_IsolateFlags::null_safety` field needs to be known upfront. The value of this field can only be determined once the kernel snapshot is available. This poses a problem in the engine because the engine used to launch the isolate at shell initialization and only need the kernel mappings later at isolate launch (when transitioning the root isolate to the `DartIsolate::Phase::Running` phase). This patch delays launch of the isolate on the UI task runner till a kernel mapping is available. The side effects of this delay (callers no longer having access to the non-running isolate handle) have been addressed in this patch. The DartIsolate API has also been amended to hide the method that could return a non-running isolate to the caller. Instead, it has been replaced with a method that requires a valid isolate configuration that returns a running root isolate. The isolate will be launched by asking the isolate configuration for its null-safety characteristics. A side effect of enabling null-safety is that Dart APIs that work with legacy types will now terminate the process if used with an isolate that has sound null-safety enabled. These APIs may no longer be used in the engine. This primarily affects the Dart Convertors in Tonic that convert certain C++ objects into the Dart counterparts. All known Dart Converters have been updated to convert C++ objects to non-nullable Dart types inferred using type traits of the corresponding C++ object. The few spots in the engine that used the old Dart APIs directly have been manually updated. To ensure that no usage of the legacy APIs remain in the engine (as these would cause runtime process terminations), the legacy APIs were prefixed with the `DART_LEGACY_API` macro and the macro defined to `[[deprecated]]` in all engine translation units. While the engine now primarily works with non-nullable Dart types, callers can still use `Dart_TypeToNonNullableType` to acquire nullable types for use directly or with Tonic. One use case that is not addressed with the Tonic Dart Convertors is the creation of non-nullable lists of nullable types. This hasn’t come up so far in the engine. A minor related change is reworking tonic to define a single library target. This allows the various tonic subsystems to depend on one another. Primarily, this is used to make the Dart convertors use the logging utilities. This now allows errors to be more descriptive as the presence of error handles is caught (and logged) earlier. Fixes https://github.com/flutter/flutter/issues/59879
238 lines
7.6 KiB
C++
238 lines
7.6 KiB
C++
// Copyright 2013 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "tonic/file_loader/file_loader.h"
|
|
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "tonic/common/macros.h"
|
|
#include "tonic/converter/dart_converter.h"
|
|
#include "tonic/filesystem/filesystem/file.h"
|
|
#include "tonic/filesystem/filesystem/path.h"
|
|
#include "tonic/filesystem/filesystem/portable_unistd.h"
|
|
#include "tonic/parsers/packages_map.h"
|
|
|
|
namespace tonic {
|
|
namespace {
|
|
|
|
constexpr char kDartScheme[] = "dart:";
|
|
|
|
constexpr char kFileScheme[] = "file:";
|
|
constexpr size_t kFileSchemeLength = sizeof(kFileScheme) - 1;
|
|
|
|
constexpr char kPackageScheme[] = "package:";
|
|
constexpr size_t kPackageSchemeLength = sizeof(kPackageScheme) - 1;
|
|
|
|
// Extract the scheme prefix ('package:' or 'file:' from )
|
|
std::string ExtractSchemePrefix(std::string url) {
|
|
if (url.find(kPackageScheme) == 0u)
|
|
return kPackageScheme;
|
|
if (url.find(kFileScheme) == 0u)
|
|
return kFileScheme;
|
|
return std::string();
|
|
}
|
|
|
|
// Extract the path from a package: or file: url.
|
|
std::string ExtractPath(std::string url) {
|
|
if (url.find(kPackageScheme) == 0u)
|
|
return url.substr(kPackageSchemeLength);
|
|
if (url.find(kFileScheme) == 0u)
|
|
return url.substr(kFileSchemeLength);
|
|
return url;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
FileLoader::FileLoader(int dirfd) : dirfd_(dirfd) {}
|
|
|
|
FileLoader::~FileLoader() {
|
|
for (auto kernel_buffer : kernel_buffers_)
|
|
free(kernel_buffer);
|
|
|
|
if (dirfd_ >= 0)
|
|
close(dirfd_);
|
|
}
|
|
|
|
std::string FileLoader::SanitizeURIEscapedCharacters(const std::string& str) {
|
|
std::string result;
|
|
result.reserve(str.size());
|
|
for (std::string::size_type i = 0; i < str.size(); ++i) {
|
|
if (str[i] == '%') {
|
|
if (i > str.size() - 3 || !isxdigit(str[i + 1]) || !isxdigit(str[i + 2]))
|
|
return "";
|
|
const std::string hex = str.substr(i + 1, 2);
|
|
const unsigned char c = strtoul(hex.c_str(), nullptr, 16);
|
|
if (!c)
|
|
return "";
|
|
result += c;
|
|
i += 2;
|
|
} else {
|
|
result += str[i];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool FileLoader::LoadPackagesMap(const std::string& packages) {
|
|
packages_ = packages;
|
|
std::string packages_source;
|
|
if (!ReadFileToString(packages_, &packages_source)) {
|
|
tonic::Log("error: Unable to load .packages file '%s'.", packages_.c_str());
|
|
return false;
|
|
}
|
|
packages_map_.reset(new PackagesMap());
|
|
std::string error;
|
|
if (!packages_map_->Parse(packages_source, &error)) {
|
|
tonic::Log("error: Unable to parse .packages file '%s'. %s",
|
|
packages_.c_str(), error.c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string FileLoader::GetFilePathForPackageURL(std::string url) {
|
|
if (!packages_map_)
|
|
return std::string();
|
|
TONIC_DCHECK(url.find(kPackageScheme) == 0u);
|
|
url = url.substr(kPackageSchemeLength);
|
|
|
|
size_t slash = url.find(FileLoader::kPathSeparator);
|
|
if (slash == std::string::npos)
|
|
return std::string();
|
|
std::string package = url.substr(0, slash);
|
|
std::string library_path = url.substr(slash + 1);
|
|
std::string package_path = packages_map_->Resolve(package);
|
|
if (package_path.empty())
|
|
return std::string();
|
|
if (package_path.find(FileLoader::kFileURLPrefix) == 0u)
|
|
return SanitizePath(package_path.substr(FileLoader::kFileURLPrefixLength) +
|
|
library_path);
|
|
return filesystem::GetDirectoryName(filesystem::AbsolutePath(packages_)) +
|
|
FileLoader::kPathSeparator + package_path +
|
|
FileLoader::kPathSeparator + library_path;
|
|
}
|
|
|
|
Dart_Handle FileLoader::HandleLibraryTag(Dart_LibraryTag tag,
|
|
Dart_Handle library,
|
|
Dart_Handle url) {
|
|
TONIC_DCHECK(Dart_IsNull(library) || Dart_IsLibrary(library) ||
|
|
Dart_IsString(library));
|
|
TONIC_DCHECK(Dart_IsString(url));
|
|
if (tag == Dart_kCanonicalizeUrl)
|
|
return CanonicalizeURL(library, url);
|
|
if (tag == Dart_kKernelTag)
|
|
return Kernel(url);
|
|
if (tag == Dart_kImportTag)
|
|
return Import(url);
|
|
return Dart_NewApiError("Unknown library tag.");
|
|
}
|
|
|
|
Dart_Handle FileLoader::CanonicalizeURL(Dart_Handle library, Dart_Handle url) {
|
|
std::string string = StdStringFromDart(url);
|
|
if (string.find(kDartScheme) == 0u)
|
|
return url;
|
|
if (string.find(kPackageScheme) == 0u)
|
|
return StdStringToDart(SanitizePath(string));
|
|
if (string.find(kFileScheme) == 0u)
|
|
return StdStringToDart(SanitizePath(CanonicalizeFileURL(string)));
|
|
|
|
std::string library_url = StdStringFromDart(Dart_LibraryUrl(library));
|
|
std::string prefix = ExtractSchemePrefix(library_url);
|
|
std::string base_path = ExtractPath(library_url);
|
|
std::string simplified_path =
|
|
filesystem::SimplifyPath(filesystem::GetDirectoryName(base_path) +
|
|
FileLoader::kPathSeparator + string);
|
|
return StdStringToDart(SanitizePath(prefix + simplified_path));
|
|
}
|
|
|
|
std::string FileLoader::GetFilePathForURL(std::string url) {
|
|
if (url.find(kPackageScheme) == 0u)
|
|
return GetFilePathForPackageURL(std::move(url));
|
|
if (url.find(kFileScheme) == 0u)
|
|
return GetFilePathForFileURL(std::move(url));
|
|
return url;
|
|
}
|
|
|
|
Dart_Handle FileLoader::FetchBytes(const std::string& url,
|
|
uint8_t*& buffer,
|
|
intptr_t& buffer_size) {
|
|
buffer = nullptr;
|
|
buffer_size = -1;
|
|
|
|
std::string path = filesystem::SimplifyPath(GetFilePathForURL(url));
|
|
if (path.empty()) {
|
|
std::string error_message = "error: Unable to read '" + url + "'.";
|
|
return Dart_NewUnhandledExceptionError(
|
|
Dart_NewStringFromCString(error_message.c_str()));
|
|
}
|
|
std::string absolute_path = filesystem::GetAbsoluteFilePath(path);
|
|
auto result = filesystem::ReadFileToBytes(absolute_path);
|
|
if (result.first == nullptr) {
|
|
std::string error_message =
|
|
"error: Unable to read '" + absolute_path + "'.";
|
|
return Dart_NewUnhandledExceptionError(
|
|
Dart_NewStringFromCString(error_message.c_str()));
|
|
}
|
|
buffer = result.first;
|
|
buffer_size = result.second;
|
|
return Dart_True();
|
|
}
|
|
|
|
Dart_Handle FileLoader::Import(Dart_Handle url) {
|
|
std::string url_string = StdStringFromDart(url);
|
|
uint8_t* buffer = nullptr;
|
|
intptr_t buffer_size = -1;
|
|
Dart_Handle result = FetchBytes(url_string, buffer, buffer_size);
|
|
if (Dart_IsError(result)) {
|
|
return result;
|
|
}
|
|
// The embedder must keep the buffer alive until isolate shutdown.
|
|
kernel_buffers_.push_back(buffer);
|
|
return Dart_LoadLibraryFromKernel(buffer, buffer_size);
|
|
}
|
|
|
|
namespace {
|
|
void MallocFinalizer(void* isolate_callback_data, void* peer) {
|
|
free(peer);
|
|
}
|
|
} // namespace
|
|
|
|
Dart_Handle FileLoader::Kernel(Dart_Handle url) {
|
|
std::string url_string = StdStringFromDart(url);
|
|
uint8_t* buffer = nullptr;
|
|
intptr_t buffer_size = -1;
|
|
Dart_Handle result = FetchBytes(url_string, buffer, buffer_size);
|
|
if (Dart_IsError(result)) {
|
|
return result;
|
|
}
|
|
result =
|
|
Dart_NewExternalTypedData(Dart_TypedData_kUint8, buffer, buffer_size);
|
|
Dart_NewFinalizableHandle(result, buffer, buffer_size, MallocFinalizer);
|
|
return result;
|
|
}
|
|
|
|
// This is invoked upon a reload request.
|
|
void FileLoader::SetPackagesUrl(Dart_Handle url) {
|
|
if (url == Dart_Null()) {
|
|
// No packages url specified.
|
|
LoadPackagesMap(packages());
|
|
return;
|
|
}
|
|
const std::string& packages_url = StdStringFromDart(url);
|
|
LoadPackagesMap(packages_url);
|
|
}
|
|
|
|
std::string FileLoader::GetFilePathForFileURL(std::string url) {
|
|
TONIC_DCHECK(url.find(FileLoader::kFileURLPrefix) == 0u);
|
|
return SanitizePath(url.substr(FileLoader::kFileURLPrefixLength));
|
|
}
|
|
|
|
std::string FileLoader::GetFileURLForPath(const std::string& path) {
|
|
return std::string(FileLoader::kFileURLPrefix) + path;
|
|
}
|
|
|
|
} // namespace tonic
|