Skip to content

Commit

Permalink
Enable lambda like native callbacks in tests and add support for cust…
Browse files Browse the repository at this point in the history
…om entrypoints. (#8299)
  • Loading branch information
chinmaygarde authored Mar 26, 2019
1 parent 2812c7d commit 78de8dc
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 3 deletions.
2 changes: 2 additions & 0 deletions shell/platform/embedder/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]

Expand Down
7 changes: 7 additions & 0 deletions shell/platform/embedder/embedder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
Expand Down
9 changes: 9 additions & 0 deletions shell/platform/embedder/embedder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions shell/platform/embedder/fixtures/simple_main.dart
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
void main() {}

@pragma('vm:entry-point')
void customEntrypoint() {
sayHiFromCustomEntrypoint();
}

void sayHiFromCustomEntrypoint() native "SayHiFromCustomEntrypoint";
9 changes: 9 additions & 0 deletions shell/platform/embedder/tests/embedder_config_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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_,
Expand Down
3 changes: 3 additions & 0 deletions shell/platform/embedder/tests/embedder_config_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,16 @@ class EmbedderConfigBuilder {

void SetIsolateCreateCallbackHook();

void SetDartEntrypoint(std::string entrypoint);

UniqueEngine LaunchEngine() const;

private:
EmbedderContext& context_;
FlutterProjectArgs project_args_ = {};
FlutterRendererConfig renderer_config_ = {};
FlutterSoftwareRendererConfig software_renderer_config_ = {};
std::string dart_entrypoint_;

FML_DISALLOW_COPY_AND_ASSIGN(EmbedderConfigBuilder);
};
Expand Down
16 changes: 15 additions & 1 deletion shell/platform/embedder/tests/embedder_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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
7 changes: 7 additions & 0 deletions shell/platform/embedder/tests/embedder_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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 {
Expand All @@ -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;
Expand All @@ -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);
};

Expand Down
90 changes: 90 additions & 0 deletions shell/platform/embedder/tests/embedder_test_resolver.cc
Original file line number Diff line number Diff line change
@@ -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 changes: 47 additions & 0 deletions shell/platform/embedder/tests/embedder_test_resolver.h
Original file line number Diff line number Diff line change
@@ -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_
18 changes: 16 additions & 2 deletions shell/platform/embedder/tests/embedder_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 78de8dc

Please sign in to comment.