From bd90a4c6ba2a0b52202441baacb23f4b1d76b5e3 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 27 Sep 2020 22:07:06 -0700 Subject: [PATCH] Enable loading snapshots with sound null safety enabled. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- common/config.gni | 1 + common/settings.h | 8 +- fml/BUILD.gn | 2 +- fml/dart/dart_converter.h | 6 +- lib/io/dart_io.cc | 3 +- lib/ui/dart_runtime_hooks.cc | 20 +- lib/ui/hooks.dart | 18 +- lib/ui/text/paragraph.cc | 22 +-- lib/ui/window/platform_configuration.h | 14 -- runtime/BUILD.gn | 3 + runtime/dart_isolate.cc | 181 ++++++++++++++---- runtime/dart_isolate.h | 79 +++++--- runtime/dart_isolate_unittests.cc | 78 ++++---- runtime/dart_lifecycle_unittests.cc | 77 +++----- runtime/dart_snapshot.cc | 14 ++ runtime/dart_snapshot.h | 3 + runtime/fixtures/runtime_test.dart | 49 ++++- .../isolate_configuration.cc | 93 +++++++-- .../isolate_configuration.h | 21 +- runtime/runtime_controller.cc | 156 +++++++-------- runtime/runtime_controller.h | 73 ++++--- runtime/type_conversions_unittests.cc | 175 +++++++++++++++++ shell/common/BUILD.gn | 2 - shell/common/engine.cc | 93 ++------- shell/common/engine.h | 11 +- shell/common/fixtures/shell_test.dart | 6 +- shell/common/run_configuration.h | 2 +- shell/platform/embedder/fixtures/main.dart | 100 ++++++++-- shell/platform/fuchsia/flutter/component.cc | 5 +- shell/platform/fuchsia/flutter/engine.cc | 7 +- shell/platform/fuchsia/flutter/engine.h | 4 +- testing/dart_isolate_runner.cc | 126 ++++++------ testing/fixture_test.cc | 13 +- testing/testing.gni | 1 + third_party/tonic/BUILD.gn | 60 +++++- third_party/tonic/common/BUILD.gn | 16 -- third_party/tonic/converter/BUILD.gn | 18 -- third_party/tonic/converter/dart_converter.h | 84 +++++++- third_party/tonic/dart_class_provider.cc | 2 +- third_party/tonic/file_loader/BUILD.gn | 30 --- third_party/tonic/file_loader/file_loader.cc | 6 +- .../tonic/file_loader/file_loader_fuchsia.cc | 4 +- .../tonic/file_loader/file_loader_posix.cc | 4 +- .../tonic/file_loader/file_loader_win.cc | 4 +- .../tonic/filesystem/filesystem/BUILD.gn | 35 ---- .../tonic/filesystem/filesystem/file.cc | 6 +- .../tonic/filesystem/filesystem/file.h | 4 +- .../tonic/filesystem/filesystem/path_posix.cc | 4 +- .../tonic/filesystem/filesystem/path_win.cc | 2 +- third_party/tonic/logging/BUILD.gn | 23 --- third_party/tonic/parsers/BUILD.gn | 16 -- third_party/tonic/scopes/BUILD.gn | 19 -- third_party/tonic/typed_data/BUILD.gn | 31 --- tools/font-subset/BUILD.gn | 2 +- 54 files changed, 1096 insertions(+), 740 deletions(-) rename {shell/common => runtime}/isolate_configuration.cc (70%) rename {shell/common => runtime}/isolate_configuration.h (90%) create mode 100644 runtime/type_conversions_unittests.cc delete mode 100644 third_party/tonic/common/BUILD.gn delete mode 100644 third_party/tonic/converter/BUILD.gn delete mode 100644 third_party/tonic/file_loader/BUILD.gn delete mode 100644 third_party/tonic/filesystem/filesystem/BUILD.gn delete mode 100644 third_party/tonic/logging/BUILD.gn delete mode 100644 third_party/tonic/parsers/BUILD.gn delete mode 100644 third_party/tonic/scopes/BUILD.gn delete mode 100644 third_party/tonic/typed_data/BUILD.gn diff --git a/common/config.gni b/common/config.gni index f7b122a3f480e..3173a5e15b069 100644 --- a/common/config.gni +++ b/common/config.gni @@ -28,6 +28,7 @@ feature_defines_list = [ "FLUTTER_RUNTIME_MODE_PROFILE=2", "FLUTTER_RUNTIME_MODE_RELEASE=3", "FLUTTER_RUNTIME_MODE_JIT_RELEASE=4", + "DART_LEGACY_API=[[deprecated]]", ] if (flutter_runtime_mode == "debug") { diff --git a/common/settings.h b/common/settings.h index d85506b946e25..17408322eedb3 100644 --- a/common/settings.h +++ b/common/settings.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -50,12 +51,11 @@ using UnhandledExceptionCallback = std::function; -// TODO(chinmaygarde): Deprecate all the "path" struct members in favor of the +// TODO(26783): Deprecate all the "path" struct members in favor of the // callback that generates the mapping from these paths. -// https://github.com/flutter/flutter/issues/26783 using MappingCallback = std::function(void)>; -using MappingsCallback = - std::function>(void)>; +using Mappings = std::vector>; +using MappingsCallback = std::function; using FrameRasterizedCallback = std::function; diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 358fbee5483ff..198d61c52c610 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -139,7 +139,7 @@ source_set("fml") { "platform/darwin/string_range_sanitization.mm", ] - libs += [ "Foundation.framework" ] + frameworks = [ "Foundation.framework" ] } if (is_android) { diff --git a/fml/dart/dart_converter.h b/fml/dart/dart_converter.h index 159ec44e25c31..ccdd2422abed6 100644 --- a/fml/dart/dart_converter.h +++ b/fml/dart/dart_converter.h @@ -23,7 +23,11 @@ struct DartConverter { return Dart_Null(); } - auto dart_list_handle = Dart_NewListOf(Dart_CoreType_Int, val->GetSize()); + auto dart_list_handle = Dart_NewListOfTypeFilled( + ToDartTypeHandle(), // type + CreateZeroInitializedDartObject(), // sentinel + val->GetSize() // size + ); if (Dart_IsError(dart_list_handle)) { FML_LOG(ERROR) << "Error while attempting to allocate a list: " diff --git a/lib/io/dart_io.cc b/lib/io/dart_io.cc index 21b5952b15233..cb255fbedd6e8 100644 --- a/lib/io/dart_io.cc +++ b/lib/io/dart_io.cc @@ -1,6 +1,7 @@ // 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. +// FLUTTER_NOLINT #include "flutter/lib/io/dart_io.h" @@ -23,7 +24,7 @@ void DartIO::InitForIsolate(bool may_insecurely_connect_to_all_domains, FML_CHECK(!LogIfError(result)); Dart_Handle embedder_config_type = - Dart_GetType(io_lib, ToDart("_EmbedderConfig"), 0, nullptr); + Dart_GetNonNullableType(io_lib, ToDart("_EmbedderConfig"), 0, nullptr); FML_CHECK(!LogIfError(embedder_config_type)); Dart_Handle allow_insecure_connections_result = Dart_SetField( diff --git a/lib/ui/dart_runtime_hooks.cc b/lib/ui/dart_runtime_hooks.cc index 3512f9b446cb8..6cfa20a5333d3 100644 --- a/lib/ui/dart_runtime_hooks.cc +++ b/lib/ui/dart_runtime_hooks.cc @@ -1,6 +1,7 @@ // 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. +// FLUTTER_NOLINT #include "flutter/lib/ui/dart_runtime_hooks.h" @@ -62,17 +63,19 @@ void DartRuntimeHooks::RegisterNatives(tonic::DartLibraryNatives* natives) { static void PropagateIfError(Dart_Handle result) { if (Dart_IsError(result)) { + FML_LOG(ERROR) << "Dart Error: " << ::Dart_GetError(result); Dart_PropagateError(result); } } -static Dart_Handle GetFunction(Dart_Handle builtin_library, const char* name) { +static Dart_Handle InvokeFunction(Dart_Handle builtin_library, + const char* name) { Dart_Handle getter_name = ToDart(name); return Dart_Invoke(builtin_library, getter_name, 0, nullptr); } static void InitDartInternal(Dart_Handle builtin_library, bool is_ui_isolate) { - Dart_Handle print = GetFunction(builtin_library, "_getPrintClosure"); + Dart_Handle print = InvokeFunction(builtin_library, "_getPrintClosure"); Dart_Handle internal_library = Dart_LookupLibrary(ToDart("dart:_internal")); @@ -112,7 +115,7 @@ static void InitDartAsync(Dart_Handle builtin_library, bool is_ui_isolate) { Dart_Handle schedule_microtask; if (is_ui_isolate) { schedule_microtask = - GetFunction(builtin_library, "_getScheduleMicrotaskClosure"); + InvokeFunction(builtin_library, "_getScheduleMicrotaskClosure"); } else { Dart_Handle isolate_lib = Dart_LookupLibrary(ToDart("dart:isolate")); Dart_Handle method_name = @@ -130,21 +133,24 @@ static void InitDartIO(Dart_Handle builtin_library, const std::string& script_uri) { Dart_Handle io_lib = Dart_LookupLibrary(ToDart("dart:io")); Dart_Handle platform_type = - Dart_GetType(io_lib, ToDart("_Platform"), 0, nullptr); + Dart_GetNonNullableType(io_lib, ToDart("_Platform"), 0, nullptr); if (!script_uri.empty()) { Dart_Handle result = Dart_SetField(platform_type, ToDart("_nativeScript"), ToDart(script_uri)); PropagateIfError(result); } - Dart_Handle locale_closure = - GetFunction(builtin_library, "_getLocaleClosure"); + // typedef _LocaleClosure = String Function(); + Dart_Handle /* _LocaleClosure? */ locale_closure = + InvokeFunction(builtin_library, "_getLocaleClosure"); + PropagateIfError(locale_closure); + // static String Function()? _localeClosure; Dart_Handle result = Dart_SetField(platform_type, ToDart("_localeClosure"), locale_closure); PropagateIfError(result); // Register dart:io service extensions used for network profiling. Dart_Handle network_profiling_type = - Dart_GetType(io_lib, ToDart("_NetworkProfiling"), 0, nullptr); + Dart_GetNonNullableType(io_lib, ToDart("_NetworkProfiling"), 0, nullptr); PropagateIfError(network_profiling_type); result = Dart_Invoke(network_profiling_type, ToDart("_registerServiceExtension"), 0, nullptr); diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index a911d79f63a8d..f3e4d1ee5ec81 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -53,15 +53,15 @@ void _updateWindowMetrics( _invoke(window.onMetricsChanged, window._onMetricsChangedZone); } -typedef _LocaleClosure = String? Function(); - -String? _localeClosure() { +String _localeClosure() { if (window.locale == null) { - return null; + return ''; } return window.locale.toString(); } +typedef _LocaleClosure = String Function(); + @pragma('vm:entry-point') // ignore: unused_element _LocaleClosure? _getLocaleClosure() => _localeClosure; @@ -210,9 +210,7 @@ void _drawFrame() { } // ignore: always_declare_return_types, prefer_generic_function_type_aliases -typedef _UnaryFunction(Null args); -// ignore: always_declare_return_types, prefer_generic_function_type_aliases -typedef _BinaryFunction(Null args, Null message); +typedef _ListStringArgFunction(List args); @pragma('vm:entry-point') // ignore: unused_element @@ -221,11 +219,7 @@ void _runMainZoned(Function startMainIsolateFunction, List args) { startMainIsolateFunction((){ runZonedGuarded(() { - if (userMainFunction is _BinaryFunction) { - // This seems to be undocumented but supported by the command line VM. - // Let's do the same in case old entry-points are ported to Flutter. - (userMainFunction as dynamic)(args, ''); - } else if (userMainFunction is _UnaryFunction) { + if (userMainFunction is _ListStringArgFunction) { (userMainFunction as dynamic)(args); } else { userMainFunction(); diff --git a/lib/ui/text/paragraph.cc b/lib/ui/text/paragraph.cc index 31897c82d9b3b..f8beda3e94899 100644 --- a/lib/ui/text/paragraph.cc +++ b/lib/ui/text/paragraph.cc @@ -1,6 +1,7 @@ // 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. +// FLUTTER_NOLINT #include "flutter/lib/ui/text/paragraph.h" @@ -132,20 +133,19 @@ tonic::Float32List Paragraph::getRectsForPlaceholders() { } Dart_Handle Paragraph::getPositionForOffset(double dx, double dy) { - Dart_Handle result = Dart_NewListOf(Dart_CoreType_Int, 2); txt::Paragraph::PositionWithAffinity pos = m_paragraph->GetGlyphPositionAtCoordinate(dx, dy); - Dart_ListSetAt(result, 0, ToDart(pos.position)); - Dart_ListSetAt(result, 1, ToDart(static_cast(pos.affinity))); - return result; + std::vector result = { + pos.position, // size_t already + static_cast(pos.affinity) // affinity (enum) + }; + return tonic::DartConverter::ToDart(result); } Dart_Handle Paragraph::getWordBoundary(unsigned offset) { txt::Paragraph::Range point = m_paragraph->GetWordBoundary(offset); - Dart_Handle result = Dart_NewListOf(Dart_CoreType_Int, 2); - Dart_ListSetAt(result, 0, ToDart(point.start)); - Dart_ListSetAt(result, 1, ToDart(point.end)); - return result; + std::vector result = {point.start, point.end}; + return tonic::DartConverter::ToDart(result); } Dart_Handle Paragraph::getLineBoundary(unsigned offset) { @@ -159,10 +159,8 @@ Dart_Handle Paragraph::getLineBoundary(unsigned offset) { break; } } - Dart_Handle result = Dart_NewListOf(Dart_CoreType_Int, 2); - Dart_ListSetAt(result, 0, ToDart(line_start)); - Dart_ListSetAt(result, 1, ToDart(line_end)); - return result; + std::vector result = {line_start, line_end}; + return tonic::DartConverter::ToDart(result); } tonic::Float64List Paragraph::computeLineMetrics() { diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 4652c7c5dcdbb..01089dac9c202 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -19,20 +19,6 @@ #include "flutter/lib/ui/window/window.h" #include "third_party/tonic/dart_persistent_value.h" -namespace tonic { -class DartLibraryNatives; - -// So tonic::ToDart> returns List instead of -// List. -template <> -struct DartListFactory { - static Dart_Handle NewList(intptr_t length) { - return Dart_NewListOf(Dart_CoreType_Int, length); - } -}; - -} // namespace tonic - namespace flutter { class FontCollection; class PlatformMessage; diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index d584693fdcfe7..9788794f7da83 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -53,6 +53,8 @@ source_set("runtime") { "dart_vm_lifecycle.h", "embedder_resources.cc", "embedder_resources.h", + "isolate_configuration.cc", + "isolate_configuration.h", "platform_data.cc", "platform_data.h", "ptrace_check.h", @@ -112,6 +114,7 @@ if (enable_unittests) { "dart_lifecycle_unittests.cc", "dart_service_isolate_unittests.cc", "dart_vm_unittests.cc", + "type_conversions_unittests.cc", ] public_configs = [ "//flutter:export_dynamic_symbols" ] diff --git a/runtime/dart_isolate.cc b/runtime/dart_isolate.cc index 919a9a532c17a..80583aab1fd6b 100644 --- a/runtime/dart_isolate.cc +++ b/runtime/dart_isolate.cc @@ -1,6 +1,7 @@ // 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. +// FLUTTER_NOLINT #include "flutter/runtime/dart_isolate.h" @@ -17,6 +18,7 @@ #include "flutter/runtime/dart_service_isolate.h" #include "flutter/runtime/dart_vm.h" #include "flutter/runtime/dart_vm_lifecycle.h" +#include "flutter/runtime/isolate_configuration.h" #include "third_party/dart/runtime/include/dart_api.h" #include "third_party/dart/runtime/include/dart_tools_api.h" #include "third_party/tonic/converter/dart_converter.h" @@ -52,6 +54,126 @@ class DartErrorString { } // anonymous namespace +DartIsolate::Flags::Flags() : Flags(nullptr) {} + +DartIsolate::Flags::Flags(const Dart_IsolateFlags* flags) { + if (flags) { + flags_ = *flags; + } else { + ::Dart_IsolateFlagsInitialize(&flags_); + } +} + +DartIsolate::Flags::~Flags() = default; + +void DartIsolate::Flags::SetNullSafetyEnabled(bool enabled) { + flags_.null_safety = enabled; +} + +Dart_IsolateFlags DartIsolate::Flags::Get() const { + return flags_; +} + +std::weak_ptr DartIsolate::CreateRunningRootIsolate( + const Settings& settings, + fml::RefPtr isolate_snapshot, + TaskRunners task_runners, + std::unique_ptr platform_configuration, + fml::WeakPtr snapshot_delegate, + fml::WeakPtr hint_freed_delegate, + fml::WeakPtr io_manager, + fml::RefPtr skia_unref_queue, + fml::WeakPtr image_decoder, + std::string advisory_script_uri, + std::string advisory_script_entrypoint, + Flags isolate_flags, + const fml::closure& isolate_create_callback, + const fml::closure& isolate_shutdown_callback, + std::optional dart_entrypoint, + std::optional dart_entrypoint_library, + std::unique_ptr isolate_configration) { + if (!isolate_snapshot) { + FML_LOG(ERROR) << "Invalid isolate snapshot."; + return {}; + } + + if (!isolate_configration) { + FML_LOG(ERROR) << "Invalid isolate configuration."; + return {}; + } + + isolate_flags.SetNullSafetyEnabled( + isolate_configration->IsNullSafetyEnabled(*isolate_snapshot)); + + auto isolate = CreateRootIsolate(settings, // + isolate_snapshot, // + task_runners, // + std::move(platform_configuration), // + snapshot_delegate, // + hint_freed_delegate, // + io_manager, // + skia_unref_queue, // + image_decoder, // + advisory_script_uri, // + advisory_script_entrypoint, // + isolate_flags, // + isolate_create_callback, // + isolate_shutdown_callback // + ) + .lock(); + + if (!isolate) { + FML_LOG(ERROR) << "Could not create root isolate."; + return {}; + } + + fml::ScopedCleanupClosure shutdown_on_error([isolate]() { + if (!isolate->Shutdown()) { + FML_DLOG(ERROR) << "Could not shutdown transient isolate."; + } + }); + + if (isolate->GetPhase() != DartIsolate::Phase::LibrariesSetup) { + FML_LOG(ERROR) << "Root isolate was created in an incorrect phase."; + return {}; + } + + if (!isolate_configration->PrepareIsolate(*isolate.get())) { + FML_LOG(ERROR) << "Could not prepare isolate."; + return {}; + } + + if (isolate->GetPhase() != DartIsolate::Phase::Ready) { + FML_LOG(ERROR) << "Root isolate not in the ready phase for Dart entrypoint " + "invocation."; + return {}; + } + + if (!isolate->RunFromLibrary(dart_entrypoint_library, // + dart_entrypoint, // + settings.dart_entrypoint_args, // + settings.root_isolate_create_callback // + )) { + FML_LOG(ERROR) << "Could not run the run main Dart entrypoint."; + return {}; + } + + if (settings.root_isolate_create_callback) { + // Isolate callbacks always occur in isolate scope. + tonic::DartState::Scope scope(isolate.get()); + settings.root_isolate_create_callback(); + } + + if (settings.root_isolate_shutdown_callback) { + isolate->AddIsolateShutdownCallback( + settings.root_isolate_shutdown_callback); + } + + shutdown_on_error.Release(); + + return isolate; +} + std::weak_ptr DartIsolate::CreateRootIsolate( const Settings& settings, fml::RefPtr isolate_snapshot, @@ -64,7 +186,7 @@ std::weak_ptr DartIsolate::CreateRootIsolate( fml::WeakPtr image_decoder, std::string advisory_script_uri, std::string advisory_script_entrypoint, - Dart_IsolateFlags* flags, + Flags flags, const fml::closure& isolate_create_callback, const fml::closure& isolate_shutdown_callback) { TRACE_EVENT0("flutter", "DartIsolate::CreateRootIsolate"); @@ -98,9 +220,10 @@ std::weak_ptr DartIsolate::CreateRootIsolate( ))); DartErrorString error; - Dart_Isolate vm_isolate = - CreateDartIsolateGroup(std::move(isolate_group_data), - std::move(isolate_data), flags, error.error()); + auto isolate_flags = flags.Get(); + Dart_Isolate vm_isolate = CreateDartIsolateGroup( + std::move(isolate_group_data), std::move(isolate_data), &isolate_flags, + error.error()); if (error) { FML_LOG(ERROR) << "CreateDartIsolateGroup failed: " << error.str(); @@ -493,40 +616,10 @@ bool DartIsolate::MarkIsolateRunnable() { return true; } -/// @note Procedure doesn't copy all closures. -[[nodiscard]] bool DartIsolate::Run(const std::string& entrypoint_name, - const std::vector& args, - const fml::closure& on_run) { - TRACE_EVENT0("flutter", "DartIsolate::Run"); - if (phase_ != Phase::Ready) { - return false; - } - - tonic::DartState::Scope scope(this); - - auto user_entrypoint_function = - Dart_GetField(Dart_RootLibrary(), tonic::ToDart(entrypoint_name.c_str())); - - auto entrypoint_args = tonic::ToDart(args); - - if (!InvokeMainEntrypoint(user_entrypoint_function, entrypoint_args)) { - return false; - } - - phase_ = Phase::Running; - - if (on_run) { - on_run(); - } - return true; -} - -/// @note Procedure doesn't copy all closures. -[[nodiscard]] bool DartIsolate::RunFromLibrary( - const std::string& library_name, - const std::string& entrypoint_name, - const std::vector& args, - const fml::closure& on_run) { +bool DartIsolate::RunFromLibrary(std::optional library_name, + std::optional entrypoint, + const std::vector& args, + const fml::closure& on_run) { TRACE_EVENT0("flutter", "DartIsolate::RunFromLibrary"); if (phase_ != Phase::Ready) { return false; @@ -534,9 +627,15 @@ bool DartIsolate::MarkIsolateRunnable() { tonic::DartState::Scope scope(this); + auto library_handle = + library_name.has_value() && !library_name.value().empty() + ? ::Dart_LookupLibrary(tonic::ToDart(library_name.value().c_str())) + : ::Dart_RootLibrary(); + auto entrypoint_handle = entrypoint.has_value() && !entrypoint.value().empty() + ? tonic::ToDart(entrypoint.value().c_str()) + : tonic::ToDart("main"); auto user_entrypoint_function = - Dart_GetField(Dart_LookupLibrary(tonic::ToDart(library_name.c_str())), - tonic::ToDart(entrypoint_name.c_str())); + ::Dart_GetField(library_handle, entrypoint_handle); auto entrypoint_args = tonic::ToDart(args); @@ -613,7 +712,7 @@ Dart_Isolate DartIsolate::DartCreateAndStartServiceIsolate( {}, // Image Decoder DART_VM_SERVICE_ISOLATE_NAME, // script uri DART_VM_SERVICE_ISOLATE_NAME, // script entrypoint - flags, // flags + DartIsolate::Flags{flags}, // flags nullptr, // isolate create callback nullptr // isolate shutdown callback ); diff --git a/runtime/dart_isolate.h b/runtime/dart_isolate.h index d1f741518ec21..b7ef2be67e3fb 100644 --- a/runtime/dart_isolate.h +++ b/runtime/dart_isolate.h @@ -6,6 +6,7 @@ #define FLUTTER_RUNTIME_DART_ISOLATE_H_ #include +#include #include #include @@ -26,6 +27,7 @@ namespace flutter { class DartVM; class DartIsolateGroupData; +class IsolateConfiguration; //------------------------------------------------------------------------------ /// @brief Represents an instance of a live isolate. An isolate is a @@ -59,6 +61,22 @@ class DartIsolateGroupData; /// class DartIsolate : public UIDartState { public: + class Flags { + public: + Flags(); + + explicit Flags(const Dart_IsolateFlags* flags); + + ~Flags(); + + void SetNullSafetyEnabled(bool enabled); + + Dart_IsolateFlags Get() const; + + private: + Dart_IsolateFlags flags_; + }; + //---------------------------------------------------------------------------- /// @brief The engine represents all dart isolates as being in one of the /// known phases. By invoking various methods on the Dart isolate, @@ -174,14 +192,19 @@ class DartIsolate : public UIDartState { /// the root isolate. The isolate is /// already in the running state at /// this point and an isolate scope is - /// current. + /// current. This callback is made for + /// all isolate launches (including + /// the children of the root isolate). /// @param[in] isolate_shutdown_callback The isolate shutdown callback. /// This will be called before the /// isolate is about to transition /// into the Shutdown phase. The /// isolate is still running at this /// point and an isolate scope is - /// current. + /// current. This callback is made + /// for all isolate shutdowns + /// (including the children of the + /// root isolate). /// /// @return A weak pointer to the root Dart isolate. The caller must /// ensure that the isolate is not referenced for long periods of @@ -189,7 +212,7 @@ class DartIsolate : public UIDartState { /// terminates itself. The caller may also only use the isolate on /// the thread on which the isolate was created. /// - static std::weak_ptr CreateRootIsolate( + static std::weak_ptr CreateRunningRootIsolate( const Settings& settings, fml::RefPtr isolate_snapshot, TaskRunners task_runners, @@ -201,9 +224,12 @@ class DartIsolate : public UIDartState { fml::WeakPtr image_decoder, std::string advisory_script_uri, std::string advisory_script_entrypoint, - Dart_IsolateFlags* flags, + Flags flags, const fml::closure& isolate_create_callback, - const fml::closure& isolate_shutdown_callback); + const fml::closure& isolate_shutdown_callback, + std::optional dart_entrypoint, + std::optional dart_entrypoint_library, + std::unique_ptr isolate_configration); // |UIDartState| ~DartIsolate() override; @@ -306,26 +332,6 @@ class DartIsolate : public UIDartState { [[nodiscard]] bool PrepareForRunningFromKernels( std::vector> kernels); - //---------------------------------------------------------------------------- - /// @brief Transition the root isolate to the `Phase::Running` phase and - /// invoke the main entrypoint (the "main" method) in the root - /// library. The isolate must already be in the `Phase::Ready` - /// phase. - /// - /// @param[in] entrypoint The entrypoint in the root library. - /// @param[in] args A list of string arguments to the entrypoint. - /// @param[in] on_run A callback to run in isolate scope after the main - /// entrypoint has been invoked. There is no isolate - /// scope current on the thread once this method - /// returns. - /// - /// @return If the isolate successfully transitioned to the running phase - /// and the main entrypoint was invoked. - /// - [[nodiscard]] bool Run(const std::string& entrypoint, - const std::vector& args, - const fml::closure& on_run = nullptr); - //---------------------------------------------------------------------------- /// @brief Transition the root isolate to the `Phase::Running` phase and /// invoke the main entrypoint (the "main" method) in the @@ -344,11 +350,12 @@ class DartIsolate : public UIDartState { /// @return If the isolate successfully transitioned to the running phase /// and the main entrypoint was invoked. /// - [[nodiscard]] bool RunFromLibrary(const std::string& library_name, - const std::string& entrypoint, + [[nodiscard]] bool RunFromLibrary(std::optional library_name, + std::optional entrypoint, const std::vector& args, const fml::closure& on_run = nullptr); + public: //---------------------------------------------------------------------------- /// @brief Transition the isolate to the `Phase::Shutdown` phase. The /// only thing left to do is to collect the isolate. @@ -384,6 +391,7 @@ class DartIsolate : public UIDartState { fml::RefPtr GetMessageHandlingTaskRunner() const; private: + friend class IsolateConfiguration; class AutoFireClosure { public: AutoFireClosure(const fml::closure& closure); @@ -403,6 +411,22 @@ class DartIsolate : public UIDartState { const bool may_insecurely_connect_to_all_domains_; std::string domain_network_policy_; + static std::weak_ptr CreateRootIsolate( + const Settings& settings, + fml::RefPtr isolate_snapshot, + TaskRunners task_runners, + std::unique_ptr platform_configuration, + fml::WeakPtr snapshot_delegate, + fml::WeakPtr hint_freed_delegate, + fml::WeakPtr io_manager, + fml::RefPtr skia_unref_queue, + fml::WeakPtr image_decoder, + std::string advisory_script_uri, + std::string advisory_script_entrypoint, + Flags flags, + const fml::closure& isolate_create_callback, + const fml::closure& isolate_shutdown_callback); + DartIsolate(const Settings& settings, TaskRunners task_runners, fml::WeakPtr snapshot_delegate, @@ -413,6 +437,7 @@ class DartIsolate : public UIDartState { std::string advisory_script_uri, std::string advisory_script_entrypoint, bool is_root_isolate); + [[nodiscard]] bool Initialize(Dart_Isolate isolate); void SetMessageHandlingTaskRunner(fml::RefPtr runner); diff --git a/runtime/dart_isolate_unittests.cc b/runtime/dart_isolate_unittests.cc index 8a1bcb2cf2469..ddbc402e563ef 100644 --- a/runtime/dart_isolate_unittests.cc +++ b/runtime/dart_isolate_unittests.cc @@ -1,6 +1,7 @@ // 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. +// FLUTTER_NOLINT #include "flutter/runtime/dart_isolate.h" @@ -10,6 +11,7 @@ #include "flutter/fml/thread.h" #include "flutter/runtime/dart_vm.h" #include "flutter/runtime/dart_vm_lifecycle.h" +#include "flutter/runtime/isolate_configuration.h" #include "flutter/testing/dart_isolate_runner.h" #include "flutter/testing/fixture_test.h" #include "flutter/testing/testing.h" @@ -46,25 +48,32 @@ TEST_F(DartIsolateTest, RootIsolateCreationAndShutdown) { GetCurrentTaskRunner(), // GetCurrentTaskRunner() // ); - auto weak_isolate = DartIsolate::CreateRootIsolate( - vm_data->GetSettings(), // settings - vm_data->GetIsolateSnapshot(), // isolate snapshot - std::move(task_runners), // task runners - nullptr, // window - {}, // snapshot delegate - {}, // hint freed delegate - {}, // io manager - {}, // unref queue - {}, // image decoder - "main.dart", // advisory uri - "main", // advisory entrypoint, - nullptr, // flags - settings.isolate_create_callback, // isolate create callback - settings.isolate_shutdown_callback // isolate shutdown callback + + auto isolate_configuration = + IsolateConfiguration::InferFromSettings(settings); + + auto weak_isolate = DartIsolate::CreateRunningRootIsolate( + vm_data->GetSettings(), // settings + vm_data->GetIsolateSnapshot(), // isolate snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // snapshot delegate + {}, // hint freed delegate + {}, // io manager + {}, // unref queue + {}, // image decoder + "main.dart", // advisory uri + "main", // advisory entrypoint, + DartIsolate::Flags{}, // flags + settings.isolate_create_callback, // isolate create callback + settings.isolate_shutdown_callback, // isolate shutdown callback + "main", // dart entrypoint + std::nullopt, // dart entrypoint library + std::move(isolate_configuration) // isolate configuration ); auto root_isolate = weak_isolate.lock(); ASSERT_TRUE(root_isolate); - ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::LibrariesSetup); + ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::Running); ASSERT_TRUE(root_isolate->Shutdown()); } @@ -81,25 +90,30 @@ TEST_F(DartIsolateTest, IsolateShutdownCallbackIsInIsolateScope) { GetCurrentTaskRunner(), // GetCurrentTaskRunner() // ); - auto weak_isolate = DartIsolate::CreateRootIsolate( - vm_data->GetSettings(), // settings - vm_data->GetIsolateSnapshot(), // isolate snapshot - std::move(task_runners), // task runners - nullptr, // window - {}, // snapshot delegate - {}, // hint freed delegate - {}, // io manager - {}, // unref queue - {}, // image decoder - "main.dart", // advisory uri - "main", // advisory entrypoint - nullptr, // flags - settings.isolate_create_callback, // isolate create callback - settings.isolate_shutdown_callback // isolate shutdown callback + auto isolate_configuration = + IsolateConfiguration::InferFromSettings(settings); + auto weak_isolate = DartIsolate::CreateRunningRootIsolate( + vm_data->GetSettings(), // settings + vm_data->GetIsolateSnapshot(), // isolate snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // snapshot delegate + {}, // hint freed delegate + {}, // io manager + {}, // unref queue + {}, // image decoder + "main.dart", // advisory uri + "main", // advisory entrypoint + DartIsolate::Flags{}, // flags + settings.isolate_create_callback, // isolate create callback + settings.isolate_shutdown_callback, // isolate shutdown callback + "main", // dart entrypoint + std::nullopt, // dart entrypoint library + std::move(isolate_configuration) // isolate configuration ); auto root_isolate = weak_isolate.lock(); ASSERT_TRUE(root_isolate); - ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::LibrariesSetup); + ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::Running); size_t destruction_callback_count = 0; root_isolate->AddIsolateShutdownCallback([&destruction_callback_count]() { ASSERT_NE(Dart_CurrentIsolate(), nullptr); diff --git a/runtime/dart_lifecycle_unittests.cc b/runtime/dart_lifecycle_unittests.cc index 3c45c702a80fe..e6fd7b1093351 100644 --- a/runtime/dart_lifecycle_unittests.cc +++ b/runtime/dart_lifecycle_unittests.cc @@ -1,6 +1,7 @@ // 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. +// FLUTTER_NOLINT #include "flutter/common/task_runners.h" #include "flutter/fml/paths.h" @@ -8,6 +9,7 @@ #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/runtime/dart_vm.h" #include "flutter/runtime/dart_vm_lifecycle.h" +#include "flutter/runtime/isolate_configuration.h" #include "flutter/testing/fixture_test.h" namespace flutter { @@ -50,57 +52,34 @@ static std::shared_ptr CreateAndRunRootIsolate( FML_CHECK(entrypoint.size() > 0); TaskRunners runners("io.flutter.test", task_runner, task_runner, task_runner, task_runner); - auto isolate_weak = DartIsolate::CreateRootIsolate( - vm.GetSettings(), // settings - vm.GetIsolateSnapshot(), // isolate_snapshot - runners, // task_runners - {}, // window - {}, // snapshot_delegate - {}, // hint_freed_delegate - {}, // io_manager - {}, // unref_queue - {}, // image_decoder - "main.dart", // advisory_script_uri - entrypoint.c_str(), // advisory_script_entrypoint - nullptr, // flags - settings.isolate_create_callback, // isolate create callback - settings.isolate_shutdown_callback // isolate shutdown callback - ); - - auto isolate = isolate_weak.lock(); - if (!isolate) { - FML_LOG(ERROR) << "Could not create valid isolate."; - return nullptr; - } - - if (DartVM::IsRunningPrecompiledCode()) { - if (!isolate->PrepareForRunningFromPrecompiledCode()) { - FML_LOG(ERROR) - << "Could not prepare to run the isolate from precompiled code."; - return nullptr; - } - - } else { - if (!isolate->PrepareForRunningFromKernels( - settings.application_kernels())) { - FML_LOG(ERROR) << "Could not prepare isolate from application kernels."; - return nullptr; - } - } + auto isolate_configuration = + IsolateConfiguration::InferFromSettings(settings); + + auto isolate = + DartIsolate::CreateRunningRootIsolate( + vm.GetSettings(), // settings + vm.GetIsolateSnapshot(), // isolate_snapshot + runners, // task_runners + {}, // window + {}, // snapshot_delegate + {}, // hint_freed_delegate + {}, // io_manager + {}, // unref_queue + {}, // image_decoder + "main.dart", // advisory_script_uri + entrypoint.c_str(), // advisory_script_entrypoint + DartIsolate::Flags{}, // flags + settings.isolate_create_callback, // isolate create callback + settings.isolate_shutdown_callback, // isolate shutdown callback, + entrypoint, // dart entrypoint + std::nullopt, // dart entrypoint library + std::move(isolate_configuration) // isolate configuration + ) + .lock(); - if (isolate->GetPhase() != DartIsolate::Phase::Ready) { - FML_LOG(ERROR) << "Isolate was not ready."; - return nullptr; - } - - if (!isolate->Run(entrypoint, {}, settings.root_isolate_create_callback)) { - FML_LOG(ERROR) << "Could not run entrypoint: " << entrypoint << "."; - return nullptr; - } - - if (isolate->GetPhase() != DartIsolate::Phase::Running) { - FML_LOG(ERROR) << "Isolate was not Running."; + if (!isolate) { + FML_LOG(ERROR) << "Could not launch the root isolate."; return nullptr; } diff --git a/runtime/dart_snapshot.cc b/runtime/dart_snapshot.cc index 050592d04a967..dd2d45c4d40f4 100644 --- a/runtime/dart_snapshot.cc +++ b/runtime/dart_snapshot.cc @@ -1,6 +1,7 @@ // 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. +// FLUTTER_NOLINT #include "flutter/runtime/dart_snapshot.h" @@ -11,6 +12,7 @@ #include "flutter/fml/trace_event.h" #include "flutter/lib/snapshot/snapshot.h" #include "flutter/runtime/dart_vm.h" +#include "third_party/dart/runtime/include/dart_api.h" namespace flutter { @@ -202,4 +204,16 @@ const uint8_t* DartSnapshot::GetInstructionsMapping() const { return instructions_ ? instructions_->GetMapping() : nullptr; } +bool DartSnapshot::IsNullSafetyEnabled(const fml::Mapping* kernel) const { + return ::Dart_DetectNullSafety( + nullptr, // script_uri (unsupported by Flutter) + nullptr, // package_config (package resolution of parent used) + nullptr, // original_working_directory (no package config) + GetDataMapping(), // snapshot_data + GetInstructionsMapping(), // snapshot_instructions + kernel ? kernel->GetMapping() : nullptr, // kernel_buffer + kernel ? kernel->GetSize() : 0u // kernel_buffer_size + ); +} + } // namespace flutter diff --git a/runtime/dart_snapshot.h b/runtime/dart_snapshot.h index 97038aac4aee9..15ca157c7a273 100644 --- a/runtime/dart_snapshot.h +++ b/runtime/dart_snapshot.h @@ -139,6 +139,9 @@ class DartSnapshot : public fml::RefCountedThreadSafe { /// const uint8_t* GetInstructionsMapping() const; + bool IsNullSafetyEnabled( + const fml::Mapping* application_kernel_mapping) const; + private: std::shared_ptr data_; std::shared_ptr instructions_; diff --git a/runtime/fixtures/runtime_test.dart b/runtime/fixtures/runtime_test.dart index 821e01e8fa433..616e026a5ccce 100644 --- a/runtime/fixtures/runtime_test.dart +++ b/runtime/fixtures/runtime_test.dart @@ -32,13 +32,7 @@ void testIsolateShutdown() { } @pragma('vm:entry-point') void testCanSaveCompilationTrace() { - List trace; - try { - trace = saveCompilationTrace(); - } catch (exception) { - print('Could not save compilation trace: ' + exception); - } - notifyResult(trace != null && trace.isNotEmpty); + notifyResult(saveCompilationTrace().isNotEmpty); } void notifyResult(bool success) native 'NotifyNative'; @@ -58,5 +52,44 @@ void testCanLaunchSecondaryIsolate() { @pragma('vm:entry-point') void testCanRecieveArguments(List args) { - notifyResult(args != null && args.length == 1 && args[0] == 'arg1'); + notifyResult(args.length == 1 && args[0] == 'arg1'); +} + +@pragma('vm:entry-point') +void trampoline() { + notifyNative(); +} + +void notifySuccess(bool success) native 'NotifySuccess'; + +@pragma('vm:entry-point') +void testCanConvertEmptyList(List args){ + notifySuccess(args.length == 0); +} + +@pragma('vm:entry-point') +void testCanConvertListOfStrings(List args){ + notifySuccess(args.length == 4 && + args[0] == 'tinker' && + args[1] == 'tailor' && + args[2] == 'soldier' && + args[3] == 'sailor'); +} + +@pragma('vm:entry-point') +void testCanConvertListOfDoubles(List args){ + notifySuccess(args.length == 4 && + args[0] == 1.0 && + args[1] == 2.0 && + args[2] == 3.0 && + args[3] == 4.0); +} + +@pragma('vm:entry-point') +void testCanConvertListOfInts(List args){ + notifySuccess(args.length == 4 && + args[0] == 1 && + args[1] == 2 && + args[2] == 3 && + args[3] == 4); } diff --git a/shell/common/isolate_configuration.cc b/runtime/isolate_configuration.cc similarity index 70% rename from shell/common/isolate_configuration.cc rename to runtime/isolate_configuration.cc index 4d37c03464ff2..a7f4aa19da5c9 100644 --- a/shell/common/isolate_configuration.cc +++ b/runtime/isolate_configuration.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/shell/common/isolate_configuration.h" +#include "flutter/runtime/isolate_configuration.h" #include "flutter/fml/make_copyable.h" #include "flutter/runtime/dart_vm.h" @@ -32,6 +32,11 @@ class AppSnapshotIsolateConfiguration final : public IsolateConfiguration { return isolate.PrepareForRunningFromPrecompiledCode(); } + // |IsolateConfiguration| + bool IsNullSafetyEnabled(const DartSnapshot& snapshot) override { + return snapshot.IsNullSafetyEnabled(nullptr); + } + private: FML_DISALLOW_COPY_AND_ASSIGN(AppSnapshotIsolateConfiguration); }; @@ -49,6 +54,11 @@ class KernelIsolateConfiguration : public IsolateConfiguration { return isolate.PrepareForRunningFromKernel(std::move(kernel_)); } + // |IsolateConfiguration| + bool IsNullSafetyEnabled(const DartSnapshot& snapshot) override { + return snapshot.IsNullSafetyEnabled(kernel_.get()); + } + private: std::unique_ptr kernel_; @@ -60,7 +70,12 @@ class KernelListIsolateConfiguration final : public IsolateConfiguration { KernelListIsolateConfiguration( std::vector>> kernel_pieces) - : kernel_pieces_(std::move(kernel_pieces)) {} + : kernel_piece_futures_(std::move(kernel_pieces)) { + if (kernel_piece_futures_.empty()) { + FML_LOG(ERROR) << "Attempted to create kernel list configuration without " + "any kernel blobs."; + } + } // |IsolateConfiguration| bool DoPrepareIsolate(DartIsolate& isolate) override { @@ -68,11 +83,22 @@ class KernelListIsolateConfiguration final : public IsolateConfiguration { return false; } - for (size_t i = 0; i < kernel_pieces_.size(); i++) { - bool last_piece = i + 1 == kernel_pieces_.size(); + ResolveKernelPiecesIfNecessary(); - if (!isolate.PrepareForRunningFromKernel(kernel_pieces_[i].get(), - last_piece)) { + if (resolved_kernel_pieces_.empty()) { + FML_DLOG(ERROR) << "No kernel pieces provided to prepare this isolate."; + return false; + } + + for (size_t i = 0; i < resolved_kernel_pieces_.size(); i++) { + if (!resolved_kernel_pieces_[i]) { + FML_DLOG(ERROR) << "This kernel list isolate configuration was already " + "used to prepare an isolate."; + return false; + } + const bool last_piece = i + 1 == resolved_kernel_pieces_.size(); + if (!isolate.PrepareForRunningFromKernel( + std::move(resolved_kernel_pieces_[i]), last_piece)) { return false; } } @@ -80,8 +106,36 @@ class KernelListIsolateConfiguration final : public IsolateConfiguration { return true; } + // |IsolateConfiguration| + bool IsNullSafetyEnabled(const DartSnapshot& snapshot) override { + ResolveKernelPiecesIfNecessary(); + const auto kernel = resolved_kernel_pieces_.empty() + ? nullptr + : resolved_kernel_pieces_.front().get(); + return snapshot.IsNullSafetyEnabled(kernel); + } + + // This must be call as late as possible before accessing any of the kernel + // pieces. This will delay blocking on the futures for as long as possible. So + // far, only Fuchsia depends on this optimization and only on the non-AOT + // configs. + void ResolveKernelPiecesIfNecessary() { + if (resolved_kernel_pieces_.size() == kernel_piece_futures_.size()) { + return; + } + + resolved_kernel_pieces_.clear(); + for (auto& piece : kernel_piece_futures_) { + // The get() call will xfer the unique pointer out and leave an empty + // future in the original vector. + resolved_kernel_pieces_.emplace_back(piece.get()); + } + } + private: - std::vector>> kernel_pieces_; + std::vector>> + kernel_piece_futures_; + std::vector> resolved_kernel_pieces_; FML_DISALLOW_COPY_AND_ASSIGN(KernelListIsolateConfiguration); }; @@ -150,10 +204,6 @@ std::unique_ptr IsolateConfiguration::InferFromSettings( return CreateForAppSnapshot(); } - if (!asset_manager) { - return nullptr; - } - if (settings.application_kernels) { return CreateForKernelList(settings.application_kernels()); } @@ -165,7 +215,13 @@ std::unique_ptr IsolateConfiguration::InferFromSettings( return nullptr; } - // Running from kernel snapshot. + if (!asset_manager) { + FML_DLOG(ERROR) << "No asset manager specified when attempting to create " + "isolate configuration."; + return nullptr; + } + + // Running from kernel snapshot. Requires asset manager. { std::unique_ptr kernel = asset_manager->GetAsMapping(settings.application_kernel_asset); @@ -174,7 +230,14 @@ std::unique_ptr IsolateConfiguration::InferFromSettings( } } - // Running from kernel divided into several pieces (for sharing). + // Running from kernel divided into several pieces (for sharing). Requires + // asset manager and io worker. + + if (!io_worker) { + FML_DLOG(ERROR) << "No IO worker specified to load kernel pieces."; + return nullptr; + } + { std::unique_ptr kernel_list = asset_manager->GetAsMapping(settings.application_kernel_list_asset); @@ -206,6 +269,10 @@ std::unique_ptr IsolateConfiguration::CreateForKernelList( std::vector> kernel_pieces) { std::vector>> pieces; for (auto& piece : kernel_pieces) { + if (!piece) { + FML_DLOG(ERROR) << "Invalid kernel piece."; + continue; + } std::promise> promise; pieces.push_back(promise.get_future()); promise.set_value(std::move(piece)); diff --git a/shell/common/isolate_configuration.h b/runtime/isolate_configuration.h similarity index 90% rename from shell/common/isolate_configuration.h rename to runtime/isolate_configuration.h index c5f286103a0d6..1fc3efef51323 100644 --- a/shell/common/isolate_configuration.h +++ b/runtime/isolate_configuration.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_SHELL_COMMON_ISOLATE_CONFIGURATION_H_ -#define FLUTTER_SHELL_COMMON_ISOLATE_CONFIGURATION_H_ +#ifndef FLUTTER_SHELL_RUNTIME_ISOLATE_CONFIGURATION_H_ +#define FLUTTER_SHELL_RUNTIME_ISOLATE_CONFIGURATION_H_ #include #include @@ -50,7 +50,10 @@ class IsolateConfiguration { /// for which this run configuration is used is collected. /// /// @param[in] settings The settings - /// @param[in] asset_manager The asset manager + /// @param[in] asset_manager The optional asset manager. This is used when + /// using the legacy settings fields that specify + /// the asset by name instead of a mappings + /// callback. /// @param[in] io_worker An optional IO worker. Specify `nullptr` is a /// worker should not be used or one is not /// available. @@ -58,10 +61,10 @@ class IsolateConfiguration { /// @return An isolate configuration if one can be inferred from the /// settings. If not, returns `nullptr`. /// - static std::unique_ptr InferFromSettings( + [[nodiscard]] static std::unique_ptr InferFromSettings( const Settings& settings, - std::shared_ptr asset_manager, - fml::RefPtr io_worker); + std::shared_ptr asset_manager = nullptr, + fml::RefPtr io_worker = nullptr); //---------------------------------------------------------------------------- /// @brief Creates an AOT isolate configuration using snapshot symbols @@ -152,7 +155,9 @@ class IsolateConfiguration { /// returns true, the engine will not move the isolate to the /// `DartIsolate::Phase::Ready` phase for subsequent run. /// - bool PrepareIsolate(DartIsolate& isolate); + [[nodiscard]] bool PrepareIsolate(DartIsolate& isolate); + + virtual bool IsNullSafetyEnabled(const DartSnapshot& snapshot) = 0; protected: virtual bool DoPrepareIsolate(DartIsolate& isolate) = 0; @@ -163,4 +168,4 @@ class IsolateConfiguration { } // namespace flutter -#endif // FLUTTER_SHELL_COMMON_ISOLATE_CONFIGURATION_H_ +#endif // FLUTTER_SHELL_RUNTIME_ISOLATE_CONFIGURATION_H_ diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 7697d70e37c0f..b2a8f7a14389f 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -1,6 +1,7 @@ // 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. +// FLUTTER_NOLINT #include "flutter/runtime/runtime_controller.h" @@ -11,6 +12,7 @@ #include "flutter/lib/ui/window/platform_configuration.h" #include "flutter/lib/ui/window/viewport_metrics.h" #include "flutter/lib/ui/window/window.h" +#include "flutter/runtime/isolate_configuration.h" #include "flutter/runtime/runtime_delegate.h" #include "third_party/tonic/dart_message_handler.h" @@ -18,10 +20,7 @@ namespace flutter { RuntimeController::RuntimeController(RuntimeDelegate& client, TaskRunners p_task_runners) - : client_(client), - vm_(nullptr), - task_runners_(p_task_runners), - weak_factory_(this) {} + : client_(client), vm_(nullptr), task_runners_(p_task_runners) {} RuntimeController::RuntimeController( RuntimeDelegate& p_client, @@ -55,82 +54,7 @@ RuntimeController::RuntimeController( platform_data_(std::move(p_platform_data)), isolate_create_callback_(p_isolate_create_callback), isolate_shutdown_callback_(p_isolate_shutdown_callback), - persistent_isolate_data_(std::move(p_persistent_isolate_data)), - weak_factory_(this) { - // Create the root isolate as soon as the runtime controller is initialized, - // but not using a synchronous way to avoid blocking the platform thread a - // long time as it is waiting while creating `Shell` on that platform thread. - // It will be run at a later point when the engine provides a run - // configuration and then runs the isolate. - create_and_config_root_isolate_ = - std::async(std::launch::deferred, [self = weak_factory_.GetWeakPtr()]() { - if (!self) { - return; - } - - auto strong_root_isolate = - DartIsolate::CreateRootIsolate( - self->vm_->GetVMData()->GetSettings(), // - self->isolate_snapshot_, // - self->task_runners_, // - std::make_unique(self.get()), // - self->snapshot_delegate_, // - self->hint_freed_delegate_, // - self->io_manager_, // - self->unref_queue_, // - self->image_decoder_, // - self->advisory_script_uri_, // - self->advisory_script_entrypoint_, // - nullptr, // - self->isolate_create_callback_, // - self->isolate_shutdown_callback_ // - ) - .lock(); - - FML_CHECK(strong_root_isolate) << "Could not create root isolate."; - - // The root isolate ivar is weak. - self->root_isolate_ = strong_root_isolate; - - strong_root_isolate->SetReturnCodeCallback([self](uint32_t code) { - if (!self) { - return; - } - - self->root_isolate_return_code_ = {true, code}; - }); - - if (auto* platform_configuration = - self->GetPlatformConfigurationIfAvailable()) { - tonic::DartState::Scope scope(strong_root_isolate); - platform_configuration->DidCreateIsolate(); - if (!self->FlushRuntimeStateToIsolate()) { - FML_DLOG(ERROR) << "Could not setup initial isolate state."; - } - } else { - FML_DCHECK(false) - << "RuntimeController created without window binding."; - } - - FML_DCHECK(Dart_CurrentIsolate() == nullptr); - - self->client_.OnRootIsolateCreated(); - return; - }); - - // We're still trying to create the root isolate as soon as possible here on - // the UI thread although it's deferred a little bit by - // std::async(std::launch::deferred, ...). So the callers of `GetRootIsolate` - // should get a quick return after this UI thread task. - task_runners_.GetUITaskRunner()->PostTask( - [self = weak_factory_.GetWeakPtr()]() { - if (!self) { - return; - } - - self->GetRootIsolate(); - }); -} + persistent_isolate_data_(std::move(p_persistent_isolate_data)) {} RuntimeController::~RuntimeController() { FML_DCHECK(Dart_CurrentIsolate() == nullptr); @@ -418,20 +342,82 @@ tonic::DartErrorHandleType RuntimeController::GetLastError() { return root_isolate ? root_isolate->GetLastError() : tonic::kNoError; } +bool RuntimeController::LaunchRootIsolate( + std::optional dart_entrypoint, + std::optional dart_entrypoint_library, + std::unique_ptr isolate_configuration) { + if (root_isolate_.lock()) { + FML_LOG(ERROR) << "Root isolate was already running."; + return false; + } + + auto strong_root_isolate = + DartIsolate::CreateRunningRootIsolate( + vm_->GetVMData()->GetSettings(), // + isolate_snapshot_, // + task_runners_, // + std::make_unique(this), // + snapshot_delegate_, // + hint_freed_delegate_, // + io_manager_, // + unref_queue_, // + image_decoder_, // + advisory_script_uri_, // + advisory_script_entrypoint_, // + DartIsolate::Flags{}, // + isolate_create_callback_, // + isolate_shutdown_callback_, // + dart_entrypoint, // + dart_entrypoint_library, // + std::move(isolate_configuration) // + ) + .lock(); + + if (!strong_root_isolate) { + FML_LOG(ERROR) << "Could not create root isolate."; + return false; + } + + // The root isolate ivar is weak. + root_isolate_ = strong_root_isolate; + + // Capture by `this` here is safe because the callback is made by the dart + // state itself. The isolate (and its Dart state) is owned by this object and + // it will be collected before this object. + strong_root_isolate->SetReturnCodeCallback( + [this](uint32_t code) { root_isolate_return_code_ = code; }); + + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { + tonic::DartState::Scope scope(strong_root_isolate); + platform_configuration->DidCreateIsolate(); + if (!FlushRuntimeStateToIsolate()) { + FML_DLOG(ERROR) << "Could not setup initial isolate state."; + } + } else { + FML_DCHECK(false) << "RuntimeController created without window binding."; + } + + FML_DCHECK(Dart_CurrentIsolate() == nullptr); + return true; +} + +std::optional RuntimeController::GetRootIsolateServiceID() const { + if (auto isolate = root_isolate_.lock()) { + return isolate->GetServiceId(); + } + return std::nullopt; +} + std::weak_ptr RuntimeController::GetRootIsolate() { std::shared_ptr root_isolate = root_isolate_.lock(); if (root_isolate) { return root_isolate_; } - // Root isolate is not yet created, get it and do some configuration. - FML_DCHECK(create_and_config_root_isolate_.valid()); - create_and_config_root_isolate_.get(); - return root_isolate_; } -std::pair RuntimeController::GetRootIsolateReturnCode() { +std::optional RuntimeController::GetRootIsolateReturnCode() { return root_isolate_return_code_; } diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index b3f1a41d3716f..9b5ef99da617c 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -96,7 +96,7 @@ class RuntimeController : public PlatformConfigurationClient { /// code in isolate scope when the VM /// is about to be notified that the /// engine is going to be idle. - /// @param[in] platform_data The window data (if exists). + /// @param[in] platform_data The window data (if exists). /// @param[in] isolate_create_callback The isolate create callback. This /// allows callers to run native code /// in isolate scope on the UI task @@ -133,10 +133,39 @@ class RuntimeController : public PlatformConfigurationClient { ~RuntimeController() override; //---------------------------------------------------------------------------- - /// @brief Clone the the runtime controller. This re-creates the root - /// isolate with the same snapshots and copies all window data to - /// the new instance. This is usually only used in the debug - /// runtime mode to support the cold-restart scenario. + /// @brief Launches the isolate using the window data associated with + /// this runtime controller. Before this call, the Dart isolate + /// has not been initialized. On successful return, the caller can + /// assume that the isolate is in the + /// `DartIsolate::Phase::Running` phase. + /// + /// This call will fail if a root isolate is already running. To + /// re-create an isolate with the window data associated with this + /// runtime controller, `Clone` this runtime controller and + /// Launch an isolate in that runtime controller instead. + /// + /// @param[in] dart_entrypoint The dart entrypoint. If + /// `std::nullopt` or empty, `main` will + /// be attempted. + /// @param[in] dart_entrypoint_library The dart entrypoint library. If + /// `std::nullopt` or empty, the core + /// library will be attempted. + /// @param[in] isolate_configuration The isolate configuration + /// + /// @return If the isolate could be launched and guided to the + /// `DartIsolate::Phase::Running` phase. + /// + [[nodiscard]] bool LaunchRootIsolate( + std::optional dart_entrypoint, + std::optional dart_entrypoint_library, + std::unique_ptr isolate_configuration); + + //---------------------------------------------------------------------------- + /// @brief Clone the the runtime controller. Launching an isolate with a + /// cloned runtime controller will use the same snapshots and + /// copies all window data to the new instance. This is usually + /// only used in the debug runtime mode to support the + /// cold-restart scenario. /// /// @return A clone of the existing runtime controller. /// @@ -428,29 +457,20 @@ class RuntimeController : public PlatformConfigurationClient { tonic::DartErrorHandleType GetLastError(); //---------------------------------------------------------------------------- - /// @brief Get a weak pointer to the root Dart isolate. This isolate may - /// only be locked on the UI task runner. Callers use this - /// accessor to transition to the root isolate to the running - /// phase. Note that it might take times if the isolate is not yet - /// created, which should be done in a subsequence task after - /// constructing `RuntimeController`, or it should get a quick - /// return otherwise. + /// @brief Get the service ID of the root isolate if the root isolate is + /// running. /// - /// @return The root isolate reference. + /// @return The root isolate service id. /// - std::weak_ptr GetRootIsolate(); + std::optional GetRootIsolateServiceID() const; //---------------------------------------------------------------------------- /// @brief Get the return code specified by the root isolate (if one is /// present). /// - /// @bug Change this method to return `std::optional` - /// instead. - /// - /// @return The root isolate return code. The first argument in the pair - /// indicates if one is specified by the root isolate. + /// @return The root isolate return code if the isolate has specified one. /// - std::pair GetRootIsolateReturnCode(); + std::optional GetRootIsolateReturnCode(); protected: /// Constructor for Mocks. @@ -489,11 +509,10 @@ class RuntimeController : public PlatformConfigurationClient { // `RuntimeController`, be careful to use it directly while it might have not // been created yet. Call `GetRootIsolate()` instead which guarantees that. std::weak_ptr root_isolate_; - std::pair root_isolate_return_code_ = {false, 0}; + std::optional root_isolate_return_code_; const fml::closure isolate_create_callback_; const fml::closure isolate_shutdown_callback_; std::shared_ptr persistent_isolate_data_; - fml::WeakPtrFactory weak_factory_; PlatformConfiguration* GetPlatformConfigurationIfAvailable(); @@ -531,6 +550,16 @@ class RuntimeController : public PlatformConfigurationClient { std::unique_ptr> ComputePlatformResolvedLocale( const std::vector& supported_locale_data) override; + //---------------------------------------------------------------------------- + /// @brief Get a weak pointer to the root Dart isolate. This isolate may + /// only be locked on the UI task runner. Callers use this + /// accessor to transition to the root isolate to the running + /// phase. + /// + /// @return The root isolate reference. + /// + std::weak_ptr GetRootIsolate(); + FML_DISALLOW_COPY_AND_ASSIGN(RuntimeController); }; diff --git a/runtime/type_conversions_unittests.cc b/runtime/type_conversions_unittests.cc new file mode 100644 index 0000000000000..fe02962a3aa56 --- /dev/null +++ b/runtime/type_conversions_unittests.cc @@ -0,0 +1,175 @@ +// 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. +// FLUTTER_NOLINT + +#include "flutter/runtime/dart_vm_lifecycle.h" +#include "flutter/testing/dart_isolate_runner.h" +#include "flutter/testing/fixture_test.h" +#include "flutter/testing/testing.h" +#include "flutter/third_party/tonic/converter/dart_converter.h" + +namespace flutter { +namespace testing { + +class TypeConversionsTest : public FixtureTest { + public: + TypeConversionsTest() + : settings_(CreateSettingsForFixture()), + vm_(DartVMRef::Create(settings_)) {} + + ~TypeConversionsTest() = default; + + [[nodiscard]] bool RunWithEntrypoint(const std::string& entrypoint) { + if (running_isolate_) { + return false; + } + auto thread = CreateNewThread(); + TaskRunners single_threaded_task_runner(GetCurrentTestName(), thread, + thread, thread, thread); + auto isolate = + RunDartCodeInIsolate(vm_, settings_, single_threaded_task_runner, + entrypoint, {}, GetFixturesPath()); + if (!isolate || isolate->get()->GetPhase() != DartIsolate::Phase::Running) { + return false; + } + + running_isolate_ = std::move(isolate); + return true; + } + + private: + Settings settings_; + DartVMRef vm_; + std::unique_ptr running_isolate_; + FML_DISALLOW_COPY_AND_ASSIGN(TypeConversionsTest); +}; + +TEST_F(TypeConversionsTest, TestFixture) { + ASSERT_TRUE(RunWithEntrypoint("main")); +} + +TEST_F(TypeConversionsTest, CanConvertEmptyList) { + fml::AutoResetWaitableEvent event; + AddNativeCallback( + "NotifySuccess", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { + auto bool_handle = Dart_GetNativeArgument(args, 0); + ASSERT_FALSE(tonic::LogIfError(bool_handle)); + ASSERT_TRUE(tonic::DartConverter::FromDart(bool_handle)); + event.Signal(); + })); + AddNativeCallback( + "NotifyNative", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments) { + std::vector items; + auto items_handle = tonic::ToDart(items); + ASSERT_FALSE(tonic::LogIfError(items_handle)); + tonic::DartInvokeField(::Dart_RootLibrary(), "testCanConvertEmptyList", + {items_handle}); + })); + ASSERT_TRUE(RunWithEntrypoint("trampoline")); + event.Wait(); +} + +TEST_F(TypeConversionsTest, CanConvertListOfStrings) { + fml::AutoResetWaitableEvent event; + AddNativeCallback( + "NotifySuccess", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { + auto bool_handle = Dart_GetNativeArgument(args, 0); + ASSERT_FALSE(tonic::LogIfError(bool_handle)); + ASSERT_TRUE(tonic::DartConverter::FromDart(bool_handle)); + event.Signal(); + })); + AddNativeCallback( + "NotifyNative", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments) { + std::vector items; + items.push_back("tinker"); + items.push_back("tailor"); + items.push_back("soldier"); + items.push_back("sailor"); + auto items_handle = tonic::ToDart(items); + ASSERT_FALSE(tonic::LogIfError(items_handle)); + tonic::DartInvokeField(::Dart_RootLibrary(), + "testCanConvertListOfStrings", {items_handle}); + })); + ASSERT_TRUE(RunWithEntrypoint("trampoline")); + event.Wait(); +} + +TEST_F(TypeConversionsTest, CanConvertListOfDoubles) { + fml::AutoResetWaitableEvent event; + AddNativeCallback( + "NotifySuccess", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { + auto bool_handle = Dart_GetNativeArgument(args, 0); + ASSERT_FALSE(tonic::LogIfError(bool_handle)); + ASSERT_TRUE(tonic::DartConverter::FromDart(bool_handle)); + event.Signal(); + })); + AddNativeCallback( + "NotifyNative", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments) { + std::vector items; + items.push_back(1.0); + items.push_back(2.0); + items.push_back(3.0); + items.push_back(4.0); + auto items_handle = tonic::ToDart(items); + ASSERT_FALSE(tonic::LogIfError(items_handle)); + tonic::DartInvokeField(::Dart_RootLibrary(), + "testCanConvertListOfDoubles", {items_handle}); + })); + ASSERT_TRUE(RunWithEntrypoint("trampoline")); + event.Wait(); +} + +TEST_F(TypeConversionsTest, CanConvertListOfInts) { + fml::AutoResetWaitableEvent event; + AddNativeCallback( + "NotifySuccess", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { + auto bool_handle = Dart_GetNativeArgument(args, 0); + ASSERT_FALSE(tonic::LogIfError(bool_handle)); + ASSERT_TRUE(tonic::DartConverter::FromDart(bool_handle)); + event.Signal(); + })); + AddNativeCallback( + "NotifyNative", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments) { + std::vector items; + items.push_back(1); + items.push_back(2); + items.push_back(3); + items.push_back(4); + auto items_handle = tonic::ToDart(items); + ASSERT_FALSE(tonic::LogIfError(items_handle)); + tonic::DartInvokeField(::Dart_RootLibrary(), "testCanConvertListOfInts", + {items_handle}); + })); + ASSERT_TRUE(RunWithEntrypoint("trampoline")); + event.Wait(); +} + +TEST_F(TypeConversionsTest, CanConvertListOfFloatsToListOfDartDoubles) { + fml::AutoResetWaitableEvent event; + AddNativeCallback( + "NotifySuccess", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { + auto bool_handle = Dart_GetNativeArgument(args, 0); + ASSERT_FALSE(tonic::LogIfError(bool_handle)); + ASSERT_TRUE(tonic::DartConverter::FromDart(bool_handle)); + event.Signal(); + })); + AddNativeCallback( + "NotifyNative", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments) { + std::vector items; + items.push_back(1.0f); + items.push_back(2.0f); + items.push_back(3.0f); + items.push_back(4.0f); + auto items_handle = tonic::ToDart(items); + ASSERT_FALSE(tonic::LogIfError(items_handle)); + // This will fail on type mismatch. + tonic::DartInvokeField(::Dart_RootLibrary(), + "testCanConvertListOfDoubles", {items_handle}); + })); + ASSERT_TRUE(RunWithEntrypoint("trampoline")); + event.Wait(); +} + +} // namespace testing +} // namespace flutter diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index eff5742eebee8..c988e88c2a1a6 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -69,8 +69,6 @@ source_set("common") { "display_manager.h", "engine.cc", "engine.h", - "isolate_configuration.cc", - "isolate_configuration.h", "persistent_cache.cc", "persistent_cache.h", "pipeline.cc", diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 310ebc4ce925b..31901318a8243 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -1,6 +1,7 @@ // 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. +// FLUTTER_NOLINT #include "flutter/shell/common/engine.h" @@ -153,90 +154,32 @@ Engine::RunStatus Engine::Run(RunConfiguration configuration) { last_entry_point_ = configuration.GetEntrypoint(); last_entry_point_library_ = configuration.GetEntrypointLibrary(); - auto isolate_launch_status = - PrepareAndLaunchIsolate(std::move(configuration)); - if (isolate_launch_status == Engine::RunStatus::Failure) { - FML_LOG(ERROR) << "Engine not prepare and launch isolate."; - return isolate_launch_status; - } else if (isolate_launch_status == - Engine::RunStatus::FailureAlreadyRunning) { - return isolate_launch_status; - } - - std::shared_ptr isolate = - runtime_controller_->GetRootIsolate().lock(); - - bool isolate_running = - isolate && isolate->GetPhase() == DartIsolate::Phase::Running; - - if (isolate_running) { - tonic::DartState::Scope scope(isolate.get()); - - if (settings_.root_isolate_create_callback) { - settings_.root_isolate_create_callback(); - } - - if (settings_.root_isolate_shutdown_callback) { - isolate->AddIsolateShutdownCallback( - settings_.root_isolate_shutdown_callback); - } - - std::string service_id = isolate->GetServiceId(); - fml::RefPtr service_id_message = - fml::MakeRefCounted( - kIsolateChannel, - std::vector(service_id.begin(), service_id.end()), - nullptr); - HandlePlatformMessage(service_id_message); - } - - return isolate_running ? Engine::RunStatus::Success - : Engine::RunStatus::Failure; -} - -Engine::RunStatus Engine::PrepareAndLaunchIsolate( - RunConfiguration configuration) { - TRACE_EVENT0("flutter", "Engine::PrepareAndLaunchIsolate"); - UpdateAssetManager(configuration.GetAssetManager()); - auto isolate_configuration = configuration.TakeIsolateConfiguration(); - - std::shared_ptr isolate = - runtime_controller_->GetRootIsolate().lock(); - - if (!isolate) { - return RunStatus::Failure; - } - - // This can happen on iOS after a plugin shows a native window and returns to - // the Flutter ViewController. - if (isolate->GetPhase() == DartIsolate::Phase::Running) { - FML_DLOG(WARNING) << "Isolate was already running!"; + if (runtime_controller_->IsRootIsolateRunning()) { return RunStatus::FailureAlreadyRunning; } - if (!isolate_configuration->PrepareIsolate(*isolate)) { - FML_LOG(ERROR) << "Could not prepare to run the isolate."; + if (!runtime_controller_->LaunchRootIsolate( + configuration.GetEntrypoint(), // + configuration.GetEntrypointLibrary(), // + configuration.TakeIsolateConfiguration()) // + ) { return RunStatus::Failure; } - if (configuration.GetEntrypointLibrary().empty()) { - if (!isolate->Run(configuration.GetEntrypoint(), - settings_.dart_entrypoint_args)) { - FML_LOG(ERROR) << "Could not run the isolate."; - return RunStatus::Failure; - } - } else { - if (!isolate->RunFromLibrary(configuration.GetEntrypointLibrary(), - configuration.GetEntrypoint(), - settings_.dart_entrypoint_args)) { - FML_LOG(ERROR) << "Could not run the isolate."; - return RunStatus::Failure; - } + auto service_id = runtime_controller_->GetRootIsolateServiceID(); + if (service_id.has_value()) { + fml::RefPtr service_id_message = + fml::MakeRefCounted( + kIsolateChannel, + std::vector(service_id.value().begin(), + service_id.value().end()), + nullptr); + HandlePlatformMessage(service_id_message); } - return RunStatus::Success; + return Engine::RunStatus::Success; } void Engine::BeginFrame(fml::TimePoint frame_time) { @@ -261,7 +204,7 @@ void Engine::NotifyIdle(int64_t deadline) { hint_freed_bytes_since_last_idle_ = 0; } -std::pair Engine::GetUIIsolateReturnCode() { +std::optional Engine::GetUIIsolateReturnCode() { return runtime_controller_->GetRootIsolateReturnCode(); } diff --git a/shell/common/engine.h b/shell/common/engine.h index c0e53c3d742a4..3319e9c9e28c5 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -608,14 +608,9 @@ class Engine final : public RuntimeDelegate, /// /// @see `UIIsolateHasLivePorts` /// - // TODO(chinmaygarde): Use std::optional instead of the pair now that it is - // available. + /// @return The return code (if specified) by the isolate. /// - /// @return A pair containing a boolean value indicating if the isolate - /// set a "return value" and that value if present. When the first - /// item of the pair is false, second item is meaningless. - /// - std::pair GetUIIsolateReturnCode(); + std::optional GetUIIsolateReturnCode(); //---------------------------------------------------------------------------- /// @brief Indicates to the Flutter application that it has obtained a @@ -837,8 +832,6 @@ class Engine final : public RuntimeDelegate, bool GetAssetAsBuffer(const std::string& name, std::vector* data); - RunStatus PrepareAndLaunchIsolate(RunConfiguration configuration); - friend class testing::ShellTest; FML_DISALLOW_COPY_AND_ASSIGN(Engine); diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index 492ddb8960d03..15d9386df12e7 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -87,7 +87,7 @@ void testCanLaunchSecondaryIsolate() { @pragma('vm:entry-point') void testSkiaResourceCacheSendsResponse() { - final PlatformMessageResponseCallback callback = (ByteData data) { + final PlatformMessageResponseCallback callback = (ByteData? data) { if (data == null) { throw 'Response must not be null.'; } @@ -130,7 +130,7 @@ void canCreateImageFromDecompressedData() { @pragma('vm:entry-point') void canAccessIsolateLaunchData() { - notifyMessage(utf8.decode(window.getPersistentIsolateData().buffer.asUint8List())); + notifyMessage(utf8.decode(window.getPersistentIsolateData()!.buffer.asUint8List())); } void notifyMessage(String string) native 'NotifyMessage'; @@ -174,7 +174,7 @@ void canAccessResourceFromAssetDir() async { window.sendPlatformMessage( 'flutter/assets', Uint8List.fromList(utf8.encode('kernel_blob.bin')).buffer.asByteData(), - (ByteData byteData) { + (ByteData? byteData) { notifyCanAccessResource(byteData != null); }, ); diff --git a/shell/common/run_configuration.h b/shell/common/run_configuration.h index 45dfed1c2d232..cb5a5b531d248 100644 --- a/shell/common/run_configuration.h +++ b/shell/common/run_configuration.h @@ -14,7 +14,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" #include "flutter/fml/unique_fd.h" -#include "flutter/shell/common/isolate_configuration.h" +#include "flutter/runtime/isolate_configuration.h" namespace flutter { diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 5dae4e494745c..1df95c1c8727c 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -79,12 +79,12 @@ class SemanticsActionData { const SemanticsActionData(this.id, this.action, this.args); final int id; final SemanticsAction action; - final ByteData args; + final ByteData? args; } Future get semanticsAction { final Completer actionReceived = Completer(); - window.onSemanticsAction = (int id, SemanticsAction action, ByteData args) { + window.onSemanticsAction = (int id, SemanticsAction action, ByteData? args) { actionReceived.complete(SemanticsActionData(id, action, args)); }; return actionReceived.future; @@ -115,12 +115,52 @@ void a11y_main() async { // ignore: non_constant_identifier_names transform: kTestTransform, childrenInTraversalOrder: Int32List.fromList([84, 96]), childrenInHitTestOrder: Int32List.fromList([96, 84]), + actions: 0, + flags: 0, + maxValueLength: 0, + currentValueLength: 0, + textSelectionBase: 0, + textSelectionExtent: 0, + platformViewId: 0, + scrollChildren: 0, + scrollIndex: 0, + scrollPosition: 0.0, + scrollExtentMax: 0.0, + scrollExtentMin: 0.0, + elevation: 0.0, + thickness: 0.0, + hint: "", + value: "", + increasedValue: "", + decreasedValue: "", + additionalActions: Int32List(0), ) ..updateNode( id: 84, label: 'B: leaf', rect: Rect.fromLTRB(40.0, 40.0, 80.0, 80.0), transform: kTestTransform, + actions: 0, + flags: 0, + maxValueLength: 0, + currentValueLength: 0, + textSelectionBase: 0, + textSelectionExtent: 0, + platformViewId: 0, + scrollChildren: 0, + scrollIndex: 0, + scrollPosition: 0.0, + scrollExtentMax: 0.0, + scrollExtentMin: 0.0, + elevation: 0.0, + thickness: 0.0, + hint: "", + value: "", + increasedValue: "", + decreasedValue: "", + additionalActions: Int32List(0), + childrenInHitTestOrder: Int32List(0), + childrenInTraversalOrder: Int32List(0), ) ..updateNode( id: 96, @@ -129,6 +169,25 @@ void a11y_main() async { // ignore: non_constant_identifier_names transform: kTestTransform, childrenInTraversalOrder: Int32List.fromList([128]), childrenInHitTestOrder: Int32List.fromList([128]), + actions: 0, + flags: 0, + maxValueLength: 0, + currentValueLength: 0, + textSelectionBase: 0, + textSelectionExtent: 0, + platformViewId: 0, + scrollChildren: 0, + scrollIndex: 0, + scrollPosition: 0.0, + scrollExtentMax: 0.0, + scrollExtentMin: 0.0, + elevation: 0.0, + thickness: 0.0, + hint: "", + value: "", + increasedValue: "", + decreasedValue: "", + additionalActions: Int32List(0), ) ..updateNode( id: 128, @@ -137,6 +196,25 @@ void a11y_main() async { // ignore: non_constant_identifier_names transform: kTestTransform, additionalActions: Int32List.fromList([21]), platformViewId: 0x3f3, + actions: 0, + flags: 0, + maxValueLength: 0, + currentValueLength: 0, + textSelectionBase: 0, + textSelectionExtent: 0, + scrollChildren: 0, + scrollIndex: 0, + scrollPosition: 0.0, + scrollExtentMax: 0.0, + scrollExtentMin: 0.0, + elevation: 0.0, + thickness: 0.0, + hint: "", + value: "", + increasedValue: "", + decreasedValue: "", + childrenInHitTestOrder: Int32List(0), + childrenInTraversalOrder: Int32List(0), ) ..updateCustomAction( id: 21, @@ -148,7 +226,7 @@ void a11y_main() async { // ignore: non_constant_identifier_names // Await semantics action from embedder. final SemanticsActionData data = await semanticsAction; - final List actionArgs = [data.args.getInt8(0), data.args.getInt8(1)]; + final List actionArgs = [data.args!.getInt8(0), data.args!.getInt8(1)]; notifySemanticsAction(data.id, data.action.index, actionArgs); // Await semantics disabled from embedder. @@ -159,20 +237,20 @@ void a11y_main() async { // ignore: non_constant_identifier_names @pragma('vm:entry-point') void platform_messages_response() { - window.onPlatformMessage = (String name, ByteData data, PlatformMessageResponseCallback callback) { - callback(data); + window.onPlatformMessage = (String name, ByteData? data, PlatformMessageResponseCallback? callback) { + callback!(data); }; signalNativeTest(); } @pragma('vm:entry-point') void platform_messages_no_response() { - window.onPlatformMessage = (String name, ByteData data, PlatformMessageResponseCallback callback) { - var list = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + window.onPlatformMessage = (String name, ByteData? data, PlatformMessageResponseCallback? callback) { + var list = data!.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); signalNativeMessage(utf8.decode(list)); // This does nothing because no one is listening on the other side. But complete the loop anyway // to make sure all null checking on response handles in the engine is in place. - callback(data); + callback!(data); }; signalNativeTest(); } @@ -180,10 +258,10 @@ void platform_messages_no_response() { @pragma('vm:entry-point') void null_platform_messages() { window.onPlatformMessage = - (String name, ByteData data, PlatformMessageResponseCallback callback) { + (String name, ByteData? data, PlatformMessageResponseCallback? callback) { // This checks if the platform_message null data is converted to Flutter null. signalNativeMessage((null == data).toString()); - callback(data); + callback!(data); }; signalNativeTest(); } @@ -507,7 +585,7 @@ void can_display_platform_view_with_pixel_ratio() { @pragma('vm:entry-point') void can_receive_locale_updates() { window.onLocaleChanged = (){ - signalNativeCount(window.locales.length); + signalNativeCount(window.locales!.length); }; signalNativeTest(); } diff --git a/shell/platform/fuchsia/flutter/component.cc b/shell/platform/fuchsia/flutter/component.cc index d60dff5c8d111..04cd15bfc6cb0 100644 --- a/shell/platform/fuchsia/flutter/component.cc +++ b/shell/platform/fuchsia/flutter/component.cc @@ -1,6 +1,7 @@ // 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. +// FLUTTER_NOLINT #include "component.h" @@ -606,8 +607,8 @@ void Application::OnEngineTerminate(const Engine* shell_holder) { // terminate when the last shell goes away. The error code return to the // application controller will be the last isolate that had an error. auto return_code = shell_holder->GetEngineReturnCode(); - if (return_code.first) { - last_return_code_ = return_code; + if (return_code.has_value()) { + last_return_code_ = {true, return_code.value()}; } shell_holders_.erase(found); diff --git a/shell/platform/fuchsia/flutter/engine.cc b/shell/platform/fuchsia/flutter/engine.cc index c0f52142c934a..0ef28c3566965 100644 --- a/shell/platform/fuchsia/flutter/engine.cc +++ b/shell/platform/fuchsia/flutter/engine.cc @@ -1,6 +1,7 @@ // 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. +// FLUTTER_NOLINT #include "engine.h" @@ -406,11 +407,11 @@ Engine::~Engine() { } } -std::pair Engine::GetEngineReturnCode() const { - std::pair code(false, 0); +std::optional Engine::GetEngineReturnCode() const { if (!shell_) { - return code; + return std::nullopt; } + std::optional code; fml::AutoResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( shell_->GetTaskRunners().GetUITaskRunner(), diff --git a/shell/platform/fuchsia/flutter/engine.h b/shell/platform/fuchsia/flutter/engine.h index 29868c731c45b..a1335c59c78e5 100644 --- a/shell/platform/fuchsia/flutter/engine.h +++ b/shell/platform/fuchsia/flutter/engine.h @@ -5,6 +5,8 @@ #ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_ENGINE_H_ #define FLUTTER_SHELL_PLATFORM_FUCHSIA_ENGINE_H_ +#include + #include #include #include @@ -56,7 +58,7 @@ class Engine final { // Returns the Dart return code for the root isolate if one is present. This // call is thread safe and synchronous. This call must be made infrequently. - std::pair GetEngineReturnCode() const; + std::optional GetEngineReturnCode() const; #if !defined(DART_PRODUCT) void WriteProfileToTrace() const; diff --git a/testing/dart_isolate_runner.cc b/testing/dart_isolate_runner.cc index 1515f2e5afb15..8ba08719bc77e 100644 --- a/testing/dart_isolate_runner.cc +++ b/testing/dart_isolate_runner.cc @@ -1,9 +1,12 @@ // 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. +// FLUTTER_NOLINT #include "flutter/testing/dart_isolate_runner.h" +#include "flutter/runtime/isolate_configuration.h" + namespace flutter { namespace testing { AutoIsolateShutdown::AutoIsolateShutdown(std::shared_ptr isolate, @@ -47,55 +50,29 @@ AutoIsolateShutdown::~AutoIsolateShutdown() { return true; } -void RunDartCodeInIsolate(DartVMRef& vm_ref, - std::unique_ptr& result, - const Settings& settings, - const TaskRunners& task_runners, - std::string entrypoint, - const std::vector& args, - const std::string& fixtures_path, - fml::WeakPtr io_manager) { +std::unique_ptr RunDartCodeInIsolateOnUITaskRunner( + DartVMRef& vm_ref, + const Settings& p_settings, + const TaskRunners& task_runners, + std::string entrypoint, + const std::vector& args, + const std::string& fixtures_path, + fml::WeakPtr io_manager) { FML_CHECK(task_runners.GetUITaskRunner()->RunsTasksOnCurrentThread()); if (!vm_ref) { - return; + return nullptr; } auto vm_data = vm_ref.GetVMData(); if (!vm_data) { - return; + return nullptr; } - auto weak_isolate = DartIsolate::CreateRootIsolate( - vm_data->GetSettings(), // settings - vm_data->GetIsolateSnapshot(), // isolate snapshot - std::move(task_runners), // task runners - nullptr, // window - {}, // snapshot delegate - {}, // hint freed delegate - io_manager, // io manager - {}, // unref queue - {}, // image decoder - "main.dart", // advisory uri - "main", // advisory entrypoint - nullptr, // flags - settings.isolate_create_callback, // isolate create callback - settings.isolate_shutdown_callback // isolate shutdown callback - ); - - auto root_isolate = std::make_unique( - weak_isolate.lock(), task_runners.GetUITaskRunner()); - - if (!root_isolate->IsValid()) { - FML_LOG(ERROR) << "Could not create isolate."; - return; - } + auto settings = p_settings; - if (root_isolate->get()->GetPhase() != DartIsolate::Phase::LibrariesSetup) { - FML_LOG(ERROR) << "Created isolate is in unexpected phase."; - return; - } + settings.dart_entrypoint_args = args; if (!DartVM::IsRunningPrecompiledCode()) { auto kernel_file_path = @@ -103,7 +80,7 @@ void RunDartCodeInIsolate(DartVMRef& vm_ref, if (!fml::IsFile(kernel_file_path)) { FML_LOG(ERROR) << "Could not locate kernel file."; - return; + return nullptr; } auto kernel_file = fml::OpenFile(kernel_file_path.c_str(), false, @@ -111,46 +88,56 @@ void RunDartCodeInIsolate(DartVMRef& vm_ref, if (!kernel_file.is_valid()) { FML_LOG(ERROR) << "Kernel file descriptor was invalid."; - return; + return nullptr; } auto kernel_mapping = std::make_unique(kernel_file); if (kernel_mapping->GetMapping() == nullptr) { FML_LOG(ERROR) << "Could not setup kernel mapping."; - return; - } - - if (!root_isolate->get()->PrepareForRunningFromKernel( - std::move(kernel_mapping))) { - FML_LOG(ERROR) - << "Could not prepare to run the isolate from the kernel file."; - return; + return nullptr; } - } else { - if (!root_isolate->get()->PrepareForRunningFromPrecompiledCode()) { - FML_LOG(ERROR) - << "Could not prepare to run the isolate from precompiled code."; - return; - } - } - if (root_isolate->get()->GetPhase() != DartIsolate::Phase::Ready) { - FML_LOG(ERROR) << "Isolate is in unexpected phase."; - return; + settings.application_kernels = fml::MakeCopyable( + [kernel_mapping = std::move(kernel_mapping)]() mutable -> Mappings { + Mappings mappings; + mappings.emplace_back(std::move(kernel_mapping)); + return mappings; + }); } - if (!root_isolate->get()->Run(entrypoint, args, - settings.root_isolate_create_callback)) { - FML_LOG(ERROR) << "Could not run the method \"" << entrypoint - << "\" in the isolate."; - return; + auto isolate_configuration = + IsolateConfiguration::InferFromSettings(settings); + + auto isolate = + DartIsolate::CreateRunningRootIsolate( + settings, // settings + vm_data->GetIsolateSnapshot(), // isolate snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // snapshot delegate + {}, // hint freed delegate + io_manager, // io manager + {}, // unref queue + {}, // image decoder + "main.dart", // advisory uri + entrypoint.c_str(), // advisory entrypoint + DartIsolate::Flags{}, // flags + settings.isolate_create_callback, // isolate create callback + settings.isolate_shutdown_callback, // isolate shutdown callback + entrypoint, // entrypoint + std::nullopt, // library + std::move(isolate_configuration) // isolate configuration + ) + .lock(); + + if (!isolate) { + FML_LOG(ERROR) << "Could not create running isolate."; + return nullptr; } - root_isolate->get()->AddIsolateShutdownCallback( - settings.root_isolate_shutdown_callback); - - result = std::move(root_isolate); + return std::make_unique(isolate, + task_runners.GetUITaskRunner()); } std::unique_ptr RunDartCodeInIsolate( @@ -165,8 +152,9 @@ std::unique_ptr RunDartCodeInIsolate( fml::AutoResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( task_runners.GetUITaskRunner(), fml::MakeCopyable([&]() mutable { - RunDartCodeInIsolate(vm_ref, result, settings, task_runners, entrypoint, - args, fixtures_path, io_manager); + result = RunDartCodeInIsolateOnUITaskRunner( + vm_ref, settings, task_runners, entrypoint, args, fixtures_path, + io_manager); latch.Signal(); })); latch.Wait(); diff --git a/testing/fixture_test.cc b/testing/fixture_test.cc index 06cb2227fdfeb..d001a8b35ab16 100644 --- a/testing/fixture_test.cc +++ b/testing/fixture_test.cc @@ -1,6 +1,7 @@ // 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. +// FLUTTER_NOLINT #include "flutter/testing/fixture_test.h" @@ -40,10 +41,16 @@ void FixtureTest::SetSnapshotsAndAssets(Settings& settings) { if (DartVM::IsRunningPrecompiledCode()) { FML_CHECK(PrepareSettingsForAOTWithSymbols(settings, aot_symbols_)); } else { - settings.application_kernels = [this]() { + settings.application_kernels = [this]() -> Mappings { std::vector> kernel_mappings; - kernel_mappings.emplace_back( - fml::FileMapping::CreateReadOnly(assets_dir_, "kernel_blob.bin")); + auto kernel_mapping = + fml::FileMapping::CreateReadOnly(assets_dir_, "kernel_blob.bin"); + if (!kernel_mapping || !kernel_mapping->IsValid()) { + FML_LOG(ERROR) << "Could not find kernel blob for test fixture not " + "running in precompiled mode."; + return kernel_mappings; + } + kernel_mappings.emplace_back(std::move(kernel_mapping)); return kernel_mappings; }; } diff --git a/testing/testing.gni b/testing/testing.gni index 488310a05032a..2e0bf8bf78963 100644 --- a/testing/testing.gni +++ b/testing/testing.gni @@ -80,6 +80,7 @@ template("dart_snapshot_kernel") { rebase_path("$root_out_dir/flutter_patched_sdk"), "--target", "flutter", + "--enable-experiment=non-nullable", "--output-dill", rebase_path(invoker.dart_kernel, root_out_dir), "--depfile", diff --git a/third_party/tonic/BUILD.gn b/third_party/tonic/BUILD.gn index eb0136d54bd59..f5bedda8dab0d 100644 --- a/third_party/tonic/BUILD.gn +++ b/third_party/tonic/BUILD.gn @@ -11,6 +11,12 @@ config("config") { source_set("tonic") { sources = [ + "common/build_config.h", + "common/log.cc", + "common/log.h", + "common/macros.h", + "converter/dart_converter.cc", + "converter/dart_converter.h", "dart_args.h", "dart_binding_macros.h", "dart_class_library.cc", @@ -32,17 +38,53 @@ source_set("tonic") { "dart_wrappable.cc", "dart_wrappable.h", "dart_wrapper_info.h", - ] + "file_loader/file_loader.cc", + "file_loader/file_loader.h", + "logging/dart_error.cc", + "logging/dart_error.h", + "logging/dart_invoke.cc", + "logging/dart_invoke.h", + "scopes/dart_api_scope.h", + "scopes/dart_isolate_scope.cc", + "scopes/dart_isolate_scope.h", + "typed_data/dart_byte_data.cc", + "typed_data/dart_byte_data.h", - public_deps = [ - "common", - "converter", - "file_loader", - "logging", - "scopes", - "typed_data", - "//third_party/dart/runtime:dart_api", + # Deprecated. + "filesystem/filesystem/eintr_wrapper.h", + "filesystem/filesystem/file.cc", + "filesystem/filesystem/file.h", + "filesystem/filesystem/path.h", + "filesystem/filesystem/portable_unistd.h", + "parsers/packages_map.cc", + "parsers/packages_map.h", + "typed_data/float32_list.h", + "typed_data/float64_list.h", + "typed_data/int32_list.h", + "typed_data/typed_list.cc", + "typed_data/typed_list.h", + "typed_data/uint16_list.h", + "typed_data/uint8_list.h", ] + if (is_win) { + sources += [ + "file_loader/file_loader_win.cc", + "filesystem/filesystem/path_win.cc", + ] + } else if (is_fuchsia) { + sources += [ + "file_loader/file_loader_fuchsia.cc", + "filesystem/filesystem/path_posix.cc", + ] + } else { + sources += [ + "file_loader/file_loader_posix.cc", + "filesystem/filesystem/path_posix.cc", + ] + } + + public_deps = [ "//third_party/dart/runtime:dart_api" ] + public_configs = [ ":config" ] } diff --git a/third_party/tonic/common/BUILD.gn b/third_party/tonic/common/BUILD.gn deleted file mode 100644 index 2af823348c554..0000000000000 --- a/third_party/tonic/common/BUILD.gn +++ /dev/null @@ -1,16 +0,0 @@ -# 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. - -source_set("common") { - visibility = [ "../*" ] - - public_configs = [ "../:config" ] - - sources = [ - "build_config.h", - "log.cc", - "log.h", - "macros.h", - ] -} diff --git a/third_party/tonic/converter/BUILD.gn b/third_party/tonic/converter/BUILD.gn deleted file mode 100644 index 1cde8c17e5ff4..0000000000000 --- a/third_party/tonic/converter/BUILD.gn +++ /dev/null @@ -1,18 +0,0 @@ -# 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. - -source_set("converter") { - visibility = [ "../*" ] - - configs += [ "../:config" ] - - sources = [ - "dart_converter.cc", - "dart_converter.h", - ] - - deps = [ "../common" ] - - public_deps = [ "//third_party/dart/runtime:dart_api" ] -} diff --git a/third_party/tonic/converter/dart_converter.h b/third_party/tonic/converter/dart_converter.h index 6693615a2d98d..2ba75a9c96aa9 100644 --- a/third_party/tonic/converter/dart_converter.h +++ b/third_party/tonic/converter/dart_converter.h @@ -6,10 +6,12 @@ #define LIB_CONVERTER_TONIC_DART_CONVERTER_H_ #include +#include #include #include "third_party/dart/runtime/include/dart_api.h" #include "tonic/common/macros.h" +#include "tonic/logging/dart_error.h" namespace tonic { @@ -279,15 +281,80 @@ struct DartConverter { //////////////////////////////////////////////////////////////////////////////// // Collections +inline Dart_Handle LookupNonNullableType(const std::string& library_name, + const std::string& type_name) { + auto library = + Dart_LookupLibrary(DartConverter::ToDart(library_name)); + if (LogIfError(library)) { + return library; + } + auto type_string = DartConverter::ToDart(type_name); + if (LogIfError(type_string)) { + return type_string; + } + auto type = Dart_GetNonNullableType(library, type_string, 0, nullptr); + if (LogIfError(type)) { + return type; + } + return type; +} + +template ::value, int> = 0> +Dart_Handle ToDartTypeHandle() { + return LookupNonNullableType("dart:core", "String"); +} + +template ::value, int> = 0> +Dart_Handle ToDartTypeHandle() { + return LookupNonNullableType("dart:core", "int"); +} + +template ::value, int> = 0> +Dart_Handle ToDartTypeHandle() { + return LookupNonNullableType("dart:core", "double"); +} + +template +Dart_Handle CreateZeroInitializedDartObject( + Dart_Handle type_handle_or_null = ::Dart_Null()) { + if constexpr (std::is_same::value) { + return ::Dart_EmptyString(); + } else if constexpr (std::is_integral::value) { + return ::Dart_NewIntegerFromUint64(0u); + } else if constexpr (std::is_floating_point::value) { + return ::Dart_NewDouble(0.0); + } else { + auto object = ::Dart_New(type_handle_or_null, ::Dart_Null(), 0, nullptr); + LogIfError(object); + return object; + } + return ::Dart_Null(); +} + template struct DartListFactory { - static Dart_Handle NewList(intptr_t length) { return Dart_NewList(length); } -}; - -template <> -struct DartListFactory { - static Dart_Handle NewList(intptr_t length) { - return Dart_NewListOf(Dart_CoreType_String, length); + static Dart_Handle NewList(Dart_Handle type_handle, intptr_t length) { + bool is_nullable = false; + auto is_nullable_handle = ::Dart_IsNullableType(type_handle, &is_nullable); + if (LogIfError(is_nullable_handle)) { + return is_nullable_handle; + } + if (is_nullable) { + auto list = ::Dart_NewListOfType(type_handle, length); + LogIfError(list); + return list; + } else { + auto sentinel = CreateZeroInitializedDartObject(type_handle); + if (LogIfError(sentinel)) { + return sentinel; + } + auto list = ::Dart_NewListOfTypeFilled(type_handle, sentinel, length); + LogIfError(list); + return list; + } + return ::Dart_Null(); } }; @@ -297,7 +364,8 @@ struct DartConverter> { using ConverterType = typename DartConverterTypes::ConverterType; static Dart_Handle ToDart(const std::vector& val) { - Dart_Handle list = DartListFactory::NewList(val.size()); + Dart_Handle list = DartListFactory::NewList( + ToDartTypeHandle(), val.size()); if (Dart_IsError(list)) return list; for (size_t i = 0; i < val.size(); i++) { diff --git a/third_party/tonic/dart_class_provider.cc b/third_party/tonic/dart_class_provider.cc index 2d9ca00642fbd..590b0b959a224 100644 --- a/third_party/tonic/dart_class_provider.cc +++ b/third_party/tonic/dart_class_provider.cc @@ -20,7 +20,7 @@ DartClassProvider::~DartClassProvider() {} Dart_Handle DartClassProvider::GetClassByName(const char* class_name) { Dart_Handle name_handle = ToDart(class_name); Dart_Handle class_handle = - Dart_GetType(library_.value(), name_handle, 0, nullptr); + Dart_GetNonNullableType(library_.value(), name_handle, 0, nullptr); TONIC_DCHECK(!Dart_IsError(class_handle)); return class_handle; } diff --git a/third_party/tonic/file_loader/BUILD.gn b/third_party/tonic/file_loader/BUILD.gn deleted file mode 100644 index e350a447c438f..0000000000000 --- a/third_party/tonic/file_loader/BUILD.gn +++ /dev/null @@ -1,30 +0,0 @@ -# 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. - -source_set("file_loader") { - visibility = [ "../*" ] - - configs += [ "../:config" ] - - sources = [ - "file_loader.cc", - "file_loader.h", - ] - - if (is_win) { - sources += [ "file_loader_win.cc" ] - } else if (is_fuchsia) { - sources += [ "file_loader_fuchsia.cc" ] - } else { - sources += [ "file_loader_posix.cc" ] - } - - deps = [ - "../common", - "../converter", - "../filesystem", - "../parsers", - "//third_party/dart/runtime:dart_api", - ] -} diff --git a/third_party/tonic/file_loader/file_loader.cc b/third_party/tonic/file_loader/file_loader.cc index 32019b2de42cd..4a9e1791794ac 100644 --- a/third_party/tonic/file_loader/file_loader.cc +++ b/third_party/tonic/file_loader/file_loader.cc @@ -8,11 +8,11 @@ #include #include -#include "filesystem/file.h" -#include "filesystem/path.h" -#include "filesystem/portable_unistd.h" #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 { diff --git a/third_party/tonic/file_loader/file_loader_fuchsia.cc b/third_party/tonic/file_loader/file_loader_fuchsia.cc index 794a298b49124..359f88df39930 100644 --- a/third_party/tonic/file_loader/file_loader_fuchsia.cc +++ b/third_party/tonic/file_loader/file_loader_fuchsia.cc @@ -13,10 +13,10 @@ #include #include -#include "filesystem/file.h" -#include "filesystem/path.h" #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/parsers/packages_map.h" namespace tonic { diff --git a/third_party/tonic/file_loader/file_loader_posix.cc b/third_party/tonic/file_loader/file_loader_posix.cc index 13f3bd3caebd3..f5167f6e7f5ca 100644 --- a/third_party/tonic/file_loader/file_loader_posix.cc +++ b/third_party/tonic/file_loader/file_loader_posix.cc @@ -8,10 +8,10 @@ #include #include -#include "filesystem/file.h" -#include "filesystem/path.h" #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/parsers/packages_map.h" namespace tonic { diff --git a/third_party/tonic/file_loader/file_loader_win.cc b/third_party/tonic/file_loader/file_loader_win.cc index e79e86f1a0439..f312a0ac1dce5 100644 --- a/third_party/tonic/file_loader/file_loader_win.cc +++ b/third_party/tonic/file_loader/file_loader_win.cc @@ -8,9 +8,9 @@ #include #include -#include "filesystem/file.h" -#include "filesystem/path.h" #include "tonic/common/macros.h" +#include "tonic/filesystem/filesystem/file.h" +#include "tonic/filesystem/filesystem/path.h" #include "tonic/parsers/packages_map.h" namespace tonic { diff --git a/third_party/tonic/filesystem/filesystem/BUILD.gn b/third_party/tonic/filesystem/filesystem/BUILD.gn deleted file mode 100644 index 197a35e907ab4..0000000000000 --- a/third_party/tonic/filesystem/filesystem/BUILD.gn +++ /dev/null @@ -1,35 +0,0 @@ -# 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. - -config("filesystem_config") { - visibility = [ ":*" ] - - # We want callers to refer to headers in this folders using the "files" - # prefix. - include_dirs = [ ".." ] -} - -source_set("filesystem") { - visibility = [ "../*" ] - - configs += [ "../../:config" ] - - sources = [ - "eintr_wrapper.h", - "file.cc", - "file.h", - "path.h", - "portable_unistd.h", - ] - - if (is_win) { - sources += [ "path_win.cc" ] - } else { - sources += [ "path_posix.cc" ] - } - - deps = [ "../../common" ] - - public_configs = [ ":filesystem_config" ] -} diff --git a/third_party/tonic/filesystem/filesystem/file.cc b/third_party/tonic/filesystem/filesystem/file.cc index 0bc6eed02b0e8..c8436c89054a3 100644 --- a/third_party/tonic/filesystem/filesystem/file.cc +++ b/third_party/tonic/filesystem/filesystem/file.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "filesystem/file.h" +#include "tonic/filesystem/filesystem/file.h" #include #include @@ -23,8 +23,8 @@ typedef SSIZE_T ssize_t; #endif -#include "filesystem/eintr_wrapper.h" -#include "filesystem/portable_unistd.h" +#include "tonic/filesystem/filesystem/eintr_wrapper.h" +#include "tonic/filesystem/filesystem/portable_unistd.h" namespace filesystem { namespace { diff --git a/third_party/tonic/filesystem/filesystem/file.h b/third_party/tonic/filesystem/filesystem/file.h index ee206850df50c..fc6e3bc166ed7 100644 --- a/third_party/tonic/filesystem/filesystem/file.h +++ b/third_party/tonic/filesystem/filesystem/file.h @@ -8,8 +8,8 @@ #include #include -#include "filesystem/eintr_wrapper.h" -#include "filesystem/portable_unistd.h" +#include "tonic/filesystem/filesystem/eintr_wrapper.h" +#include "tonic/filesystem/filesystem/portable_unistd.h" namespace filesystem { diff --git a/third_party/tonic/filesystem/filesystem/path_posix.cc b/third_party/tonic/filesystem/filesystem/path_posix.cc index ac4b6ac69b3f6..bdce0852bf87c 100644 --- a/third_party/tonic/filesystem/filesystem/path_posix.cc +++ b/third_party/tonic/filesystem/filesystem/path_posix.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "filesystem/path.h" +#include "tonic/filesystem/filesystem/path.h" #include #include @@ -17,8 +17,8 @@ #include #include -#include "filesystem/portable_unistd.h" #include "tonic/common/build_config.h" +#include "tonic/filesystem/filesystem/portable_unistd.h" namespace filesystem { namespace { diff --git a/third_party/tonic/filesystem/filesystem/path_win.cc b/third_party/tonic/filesystem/filesystem/path_win.cc index 04a5e681025d6..9b74f07a3d0bc 100644 --- a/third_party/tonic/filesystem/filesystem/path_win.cc +++ b/third_party/tonic/filesystem/filesystem/path_win.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "filesystem/path.h" +#include "tonic/filesystem/filesystem/path.h" #include diff --git a/third_party/tonic/logging/BUILD.gn b/third_party/tonic/logging/BUILD.gn deleted file mode 100644 index cee90880f6070..0000000000000 --- a/third_party/tonic/logging/BUILD.gn +++ /dev/null @@ -1,23 +0,0 @@ -# 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. - -source_set("logging") { - visibility = [ "../*" ] - - configs += [ "../:config" ] - - sources = [ - "dart_error.cc", - "dart_error.h", - "dart_invoke.cc", - "dart_invoke.h", - ] - - deps = [ - "../common", - "../converter", - ] - - public_deps = [ "//third_party/dart/runtime:dart_api" ] -} diff --git a/third_party/tonic/parsers/BUILD.gn b/third_party/tonic/parsers/BUILD.gn deleted file mode 100644 index b03ae32a1363b..0000000000000 --- a/third_party/tonic/parsers/BUILD.gn +++ /dev/null @@ -1,16 +0,0 @@ -# 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. - -source_set("parsers") { - visibility = [ "../*" ] - - configs += [ "../:config" ] - - deps = [ "../common" ] - - sources = [ - "packages_map.cc", - "packages_map.h", - ] -} diff --git a/third_party/tonic/scopes/BUILD.gn b/third_party/tonic/scopes/BUILD.gn deleted file mode 100644 index af2a3a9fc6f5c..0000000000000 --- a/third_party/tonic/scopes/BUILD.gn +++ /dev/null @@ -1,19 +0,0 @@ -# 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. - -source_set("scopes") { - visibility = [ "../*" ] - - configs += [ "../:config" ] - - sources = [ - "dart_api_scope.h", - "dart_isolate_scope.cc", - "dart_isolate_scope.h", - ] - - deps = [ "../common" ] - - public_deps = [ "//third_party/dart/runtime:dart_api" ] -} diff --git a/third_party/tonic/typed_data/BUILD.gn b/third_party/tonic/typed_data/BUILD.gn deleted file mode 100644 index cdc58d43afa62..0000000000000 --- a/third_party/tonic/typed_data/BUILD.gn +++ /dev/null @@ -1,31 +0,0 @@ -# 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. - -source_set("typed_data") { - visibility = [ "../*" ] - - configs += [ "../:config" ] - - sources = [ - "dart_byte_data.cc", - "dart_byte_data.h", - "typed_list.cc", - "typed_list.h", - - # Deprecated. - "float32_list.h", - "float64_list.h", - "int32_list.h", - "uint16_list.h", - "uint8_list.h", - ] - - deps = [ "../common" ] - - public_deps = [ - "../converter", - "../logging", - "//third_party/dart/runtime:dart_api", - ] -} diff --git a/tools/font-subset/BUILD.gn b/tools/font-subset/BUILD.gn index 54c5f75e3a101..42341c09f7614 100644 --- a/tools/font-subset/BUILD.gn +++ b/tools/font-subset/BUILD.gn @@ -12,7 +12,7 @@ executable("font-subset") { libs = [] if (is_mac) { - libs += [ + frameworks = [ "Foundation.framework", "CoreGraphics.framework", "CoreText.framework",