mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Enable lambda like native callbacks in tests and add support for custom entrypoints. (#8299)
This commit is contained in:
parent
2812c7db4a
commit
78de8dcb42
@ -74,6 +74,8 @@ executable("embedder_unittests") {
|
||||
"tests/embedder_context.h",
|
||||
"tests/embedder_test.cc",
|
||||
"tests/embedder_test.h",
|
||||
"tests/embedder_test_resolver.cc",
|
||||
"tests/embedder_test_resolver.h",
|
||||
"tests/embedder_unittests.cc",
|
||||
]
|
||||
|
||||
|
||||
@ -624,6 +624,13 @@ FlutterEngineResult FlutterEngineRun(size_t version,
|
||||
// Step 3: Run the engine.
|
||||
auto run_configuration = shell::RunConfiguration::InferFromSettings(settings);
|
||||
|
||||
if (SAFE_ACCESS(args, custom_dart_entrypoint, nullptr) != nullptr) {
|
||||
auto dart_entrypoint = std::string{args->custom_dart_entrypoint};
|
||||
if (dart_entrypoint.size() != 0) {
|
||||
run_configuration.SetEntrypoint(std::move(dart_entrypoint));
|
||||
}
|
||||
}
|
||||
|
||||
run_configuration.AddAssetResolver(
|
||||
std::make_unique<blink::DirectoryAssetBundle>(
|
||||
fml::Duplicate(settings.assets_dir)));
|
||||
|
||||
@ -563,6 +563,15 @@ typedef struct {
|
||||
// internal engine-managed thread. If the components accessed on the embedder
|
||||
// are not thread safe, the appropriate re-threading must be done.
|
||||
VsyncCallback vsync_callback;
|
||||
|
||||
// The name of a custom Dart entrypoint. This is optional and specifying a
|
||||
// null or empty entrypoint makes the engine look for a method named "main" in
|
||||
// the root library of the application.
|
||||
//
|
||||
// Care must be taken to ensure that the custom entrypoint is not tree-shaken
|
||||
// away. Usually, this is done using the `@pragma('vm:entry-point')`
|
||||
// decoration.
|
||||
const char* custom_dart_entrypoint;
|
||||
} FlutterProjectArgs;
|
||||
|
||||
FLUTTER_EXPORT
|
||||
|
||||
@ -1 +1,8 @@
|
||||
void main() {}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void customEntrypoint() {
|
||||
sayHiFromCustomEntrypoint();
|
||||
}
|
||||
|
||||
void sayHiFromCustomEntrypoint() native "SayHiFromCustomEntrypoint";
|
||||
|
||||
@ -62,6 +62,15 @@ void EmbedderConfigBuilder::SetIsolateCreateCallbackHook() {
|
||||
EmbedderContext::GetIsolateCreateCallbackHook();
|
||||
}
|
||||
|
||||
void EmbedderConfigBuilder::SetDartEntrypoint(std::string entrypoint) {
|
||||
if (entrypoint.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dart_entrypoint_ = std::move(entrypoint);
|
||||
project_args_.custom_dart_entrypoint = dart_entrypoint_.c_str();
|
||||
}
|
||||
|
||||
UniqueEngine EmbedderConfigBuilder::LaunchEngine() const {
|
||||
FlutterEngine engine = nullptr;
|
||||
auto result = FlutterEngineRun(FLUTTER_ENGINE_VERSION, &renderer_config_,
|
||||
|
||||
@ -48,6 +48,8 @@ class EmbedderConfigBuilder {
|
||||
|
||||
void SetIsolateCreateCallbackHook();
|
||||
|
||||
void SetDartEntrypoint(std::string entrypoint);
|
||||
|
||||
UniqueEngine LaunchEngine() const;
|
||||
|
||||
private:
|
||||
@ -55,6 +57,7 @@ class EmbedderConfigBuilder {
|
||||
FlutterProjectArgs project_args_ = {};
|
||||
FlutterRendererConfig renderer_config_ = {};
|
||||
FlutterSoftwareRendererConfig software_renderer_config_ = {};
|
||||
std::string dart_entrypoint_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderConfigBuilder);
|
||||
};
|
||||
|
||||
@ -36,7 +36,8 @@ static std::unique_ptr<fml::Mapping> GetMapping(const fml::UniqueFD& directory,
|
||||
}
|
||||
|
||||
EmbedderContext::EmbedderContext(std::string assets_path)
|
||||
: assets_path_(std::move(assets_path)) {
|
||||
: assets_path_(std::move(assets_path)),
|
||||
native_resolver_(std::make_shared<EmbedderTestResolver>()) {
|
||||
auto assets_dir = fml::OpenDirectory(assets_path_.c_str(), false,
|
||||
fml::FilePermission::kRead);
|
||||
vm_snapshot_data_ = GetMapping(assets_dir, "vm_snapshot_data", false);
|
||||
@ -49,6 +50,14 @@ EmbedderContext::EmbedderContext(std::string assets_path)
|
||||
isolate_snapshot_instructions_ =
|
||||
GetMapping(assets_dir, "isolate_snapshot_instr", true);
|
||||
}
|
||||
|
||||
isolate_create_callbacks_.push_back(
|
||||
[weak_resolver =
|
||||
std::weak_ptr<EmbedderTestResolver>{native_resolver_}]() {
|
||||
if (auto resolver = weak_resolver.lock()) {
|
||||
resolver->SetNativeResolverForIsolate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
EmbedderContext::~EmbedderContext() = default;
|
||||
@ -91,5 +100,10 @@ void EmbedderContext::FireIsolateCreateCallbacks() {
|
||||
}
|
||||
}
|
||||
|
||||
void EmbedderContext::AddNativeCallback(const char* name,
|
||||
Dart_NativeFunction function) {
|
||||
native_resolver_->AddNativeCallback({name}, function);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace shell
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_CONTEXT_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_CONTEXT_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -13,6 +14,7 @@
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "flutter/fml/mapping.h"
|
||||
#include "flutter/shell/platform/embedder/embedder.h"
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test_resolver.h"
|
||||
|
||||
namespace shell {
|
||||
namespace testing {
|
||||
@ -35,6 +37,8 @@ class EmbedderContext {
|
||||
|
||||
void AddIsolateCreateCallback(fml::closure closure);
|
||||
|
||||
void AddNativeCallback(const char* name, Dart_NativeFunction function);
|
||||
|
||||
private:
|
||||
// This allows the builder to access the hooks.
|
||||
friend class EmbedderConfigBuilder;
|
||||
@ -45,11 +49,14 @@ class EmbedderContext {
|
||||
std::unique_ptr<fml::Mapping> isolate_snapshot_data_;
|
||||
std::unique_ptr<fml::Mapping> isolate_snapshot_instructions_;
|
||||
std::vector<fml::closure> isolate_create_callbacks_;
|
||||
std::shared_ptr<EmbedderTestResolver> native_resolver_;
|
||||
|
||||
static VoidCallback GetIsolateCreateCallbackHook();
|
||||
|
||||
void FireIsolateCreateCallbacks();
|
||||
|
||||
void SetNativeResolver();
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderContext);
|
||||
};
|
||||
|
||||
|
||||
90
shell/platform/embedder/tests/embedder_test_resolver.cc
Normal file
90
shell/platform/embedder/tests/embedder_test_resolver.cc
Normal file
@ -0,0 +1,90 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "flutter/shell/platform/embedder/tests/embedder_test_resolver.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/fml/synchronization/thread_annotations.h"
|
||||
#include "tonic/converter/dart_converter.h"
|
||||
|
||||
namespace shell {
|
||||
namespace testing {
|
||||
|
||||
EmbedderTestResolver::EmbedderTestResolver() = default;
|
||||
|
||||
EmbedderTestResolver::~EmbedderTestResolver() = default;
|
||||
|
||||
void EmbedderTestResolver::AddNativeCallback(std::string name,
|
||||
Dart_NativeFunction callback) {
|
||||
native_callbacks_[name] = callback;
|
||||
}
|
||||
|
||||
Dart_NativeFunction EmbedderTestResolver::ResolveCallback(
|
||||
std::string name) const {
|
||||
auto found = native_callbacks_.find(name);
|
||||
if (found == native_callbacks_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return found->second;
|
||||
}
|
||||
|
||||
static std::mutex gIsolateResolversMutex;
|
||||
static std::map<Dart_Isolate, std::weak_ptr<EmbedderTestResolver>>
|
||||
gIsolateResolvers FML_GUARDED_BY(gIsolateResolversMutex);
|
||||
|
||||
Dart_NativeFunction EmbedderTestResolver::DartNativeEntryResolverCallback(
|
||||
Dart_Handle dart_name,
|
||||
int num_of_arguments,
|
||||
bool* auto_setup_scope) {
|
||||
auto name = tonic::StdStringFromDart(dart_name);
|
||||
|
||||
std::lock_guard<std::mutex> lock(gIsolateResolversMutex);
|
||||
auto found = gIsolateResolvers.find(Dart_CurrentIsolate());
|
||||
if (found == gIsolateResolvers.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (auto resolver = found->second.lock()) {
|
||||
return resolver->ResolveCallback(std::move(name));
|
||||
} else {
|
||||
gIsolateResolvers.erase(found);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const uint8_t* DartNativeEntrySymbolCallback(
|
||||
Dart_NativeFunction function) {
|
||||
return reinterpret_cast<const uint8_t*>("¯\\_(ツ)_/¯");
|
||||
}
|
||||
|
||||
void EmbedderTestResolver::SetNativeResolverForIsolate() {
|
||||
auto result = Dart_SetNativeResolver(Dart_RootLibrary(),
|
||||
DartNativeEntryResolverCallback,
|
||||
DartNativeEntrySymbolCallback);
|
||||
|
||||
if (Dart_IsError(result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(gIsolateResolversMutex);
|
||||
gIsolateResolvers[Dart_CurrentIsolate()] = shared_from_this();
|
||||
|
||||
std::vector<Dart_Isolate> isolates_with_dead_resolvers;
|
||||
for (const auto& entry : gIsolateResolvers) {
|
||||
if (!entry.second.lock()) {
|
||||
isolates_with_dead_resolvers.push_back(entry.first);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& dead_isolate : isolates_with_dead_resolvers) {
|
||||
gIsolateResolvers.erase(dead_isolate);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace shell
|
||||
47
shell/platform/embedder/tests/embedder_test_resolver.h
Normal file
47
shell/platform/embedder/tests/embedder_test_resolver.h
Normal file
@ -0,0 +1,47 @@
|
||||
// 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.
|
||||
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_TEST_RESOLVER_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_TEST_RESOLVER_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "third_party/dart/runtime/include/dart_api.h"
|
||||
|
||||
namespace shell {
|
||||
namespace testing {
|
||||
|
||||
class EmbedderTestResolver
|
||||
: public std::enable_shared_from_this<EmbedderTestResolver> {
|
||||
public:
|
||||
EmbedderTestResolver();
|
||||
|
||||
~EmbedderTestResolver();
|
||||
|
||||
void AddNativeCallback(std::string name, Dart_NativeFunction callback);
|
||||
|
||||
private:
|
||||
// Friend so that the context can set the native resolver.
|
||||
friend class EmbedderContext;
|
||||
|
||||
std::map<std::string, Dart_NativeFunction> native_callbacks_;
|
||||
|
||||
void SetNativeResolverForIsolate();
|
||||
|
||||
Dart_NativeFunction ResolveCallback(std::string name) const;
|
||||
|
||||
static Dart_NativeFunction DartNativeEntryResolverCallback(
|
||||
Dart_Handle dart_name,
|
||||
int num_of_arguments,
|
||||
bool* auto_setup_scope);
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTestResolver);
|
||||
};
|
||||
|
||||
} // namespace testing
|
||||
} // namespace shell
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_TEST_RESOLVER_H_
|
||||
@ -38,12 +38,26 @@ TEST_F(EmbedderTest, CanLaunchAndShutdownWithValidProjectArgs) {
|
||||
|
||||
TEST_F(EmbedderTest, CanLaunchAndShutdownMultipleTimes) {
|
||||
EmbedderConfigBuilder builder(GetEmbedderContext());
|
||||
for (size_t i = 0; i < 100; ++i) {
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
auto engine = builder.LaunchEngine();
|
||||
ASSERT_TRUE(engine.is_valid());
|
||||
FML_LOG(INFO) << "Engine launch count: " << i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, CanInvokeCustomEntrypoint) {
|
||||
auto& context = GetEmbedderContext();
|
||||
static fml::AutoResetWaitableEvent latch;
|
||||
Dart_NativeFunction entrypoint = [](Dart_NativeArguments args) {
|
||||
latch.Signal();
|
||||
};
|
||||
context.AddNativeCallback("SayHiFromCustomEntrypoint", entrypoint);
|
||||
EmbedderConfigBuilder builder(context);
|
||||
builder.SetDartEntrypoint("customEntrypoint");
|
||||
auto engine = builder.LaunchEngine();
|
||||
latch.Wait();
|
||||
ASSERT_TRUE(engine.is_valid());
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace shell
|
||||
} // namespace shell
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user