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. (flutter#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.