Enable lambda like native callbacks in tests and add support for custom entrypoints. (#8299)

This commit is contained in:
Chinmay Garde 2019-03-26 11:29:08 -07:00 committed by GitHub
parent 2812c7db4a
commit 78de8dcb42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 212 additions and 3 deletions

View File

@ -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",
]

View File

@ -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)));

View File

@ -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

View File

@ -1 +1,8 @@
void main() {}
@pragma('vm:entry-point')
void customEntrypoint() {
sayHiFromCustomEntrypoint();
}
void sayHiFromCustomEntrypoint() native "SayHiFromCustomEntrypoint";

View File

@ -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_,

View File

@ -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);
};

View File

@ -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

View File

@ -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);
};

View 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

View 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_

View File

@ -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