Skip to content

Commit

Permalink
Wrap the user entrypoint function in a zone with native exception cal…
Browse files Browse the repository at this point in the history
…lback. (flutter#7512)
  • Loading branch information
chinmaygarde authored Jan 17, 2019
1 parent 1b0d09b commit 25559ed
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 48 deletions.
8 changes: 8 additions & 0 deletions common/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ namespace blink {
using TaskObserverAdd =
std::function<void(intptr_t /* key */, fml::closure /* callback */)>;
using TaskObserverRemove = std::function<void(intptr_t /* key */)>;
using UnhandledExceptionCallback =
std::function<bool(const std::string& /* error */,
const std::string& /* stack trace */)>;

struct Settings {
Settings();
Expand Down Expand Up @@ -80,6 +83,11 @@ struct Settings {
// as fast as possible in returning from this callback. Long running
// operations in this callback do have the capability of introducing jank.
std::function<void(int64_t)> idle_notification_callback;
// A callback given to the embedder to react to unhandled exceptions in the
// running Flutter application. This callback is made on an internal engine
// managed thread and embedders must thread as necessary. Performing blocking
// calls in this callback will cause applications to jank.
UnhandledExceptionCallback unhandled_exception_callback;
bool enable_software_rendering = false;
bool skia_deterministic_rendering_on_cpu = false;
bool verbose_logging = false;
Expand Down
14 changes: 14 additions & 0 deletions lib/ui/hooks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,20 @@ void _drawFrame() {
_invoke(window.onDrawFrame, window._onDrawFrameZone);
}

@pragma('vm:entry-point')
// ignore: unused_element
void _runMainZoned(Function startMainIsolateFunction, Function userMainFunction) {
startMainIsolateFunction((){
runZoned<Future<void>>(() {
userMainFunction();
}, onError: (Object error, StackTrace stackTrace) {
_reportUnhandledException(error.toString(), stackTrace.toString());
});
}, null);
}

void _reportUnhandledException(String error, String stackTrace) native 'Window_reportUnhandledException';

/// Invokes [callback] inside the given [zone].
void _invoke(void callback(), Zone zone) {
if (callback == null)
Expand Down
34 changes: 25 additions & 9 deletions lib/ui/ui_dart_state.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@ using tonic::ToDart;

namespace blink {

UIDartState::UIDartState(TaskRunners task_runners,
TaskObserverAdd add_callback,
TaskObserverRemove remove_callback,
fml::WeakPtr<SnapshotDelegate> snapshot_delegate,
fml::WeakPtr<IOManager> io_manager,
std::string advisory_script_uri,
std::string advisory_script_entrypoint,
std::string logger_prefix,
IsolateNameServer* isolate_name_server)
UIDartState::UIDartState(
TaskRunners task_runners,
TaskObserverAdd add_callback,
TaskObserverRemove remove_callback,
fml::WeakPtr<SnapshotDelegate> snapshot_delegate,
fml::WeakPtr<IOManager> io_manager,
std::string advisory_script_uri,
std::string advisory_script_entrypoint,
std::string logger_prefix,
UnhandledExceptionCallback unhandled_exception_callback,
IsolateNameServer* isolate_name_server)
: task_runners_(std::move(task_runners)),
add_callback_(std::move(add_callback)),
remove_callback_(std::move(remove_callback)),
Expand All @@ -30,6 +32,7 @@ UIDartState::UIDartState(TaskRunners task_runners,
advisory_script_uri_(std::move(advisory_script_uri)),
advisory_script_entrypoint_(std::move(advisory_script_entrypoint)),
logger_prefix_(std::move(logger_prefix)),
unhandled_exception_callback_(unhandled_exception_callback),
isolate_name_server_(isolate_name_server) {
AddOrRemoveTaskObserver(true /* add */);
}
Expand Down Expand Up @@ -133,4 +136,17 @@ tonic::DartErrorHandleType UIDartState::GetLastError() {
return error;
}

void UIDartState::ReportUnhandledException(const std::string& error,
const std::string& stack_trace) {
if (unhandled_exception_callback_ &&
unhandled_exception_callback_(error, stack_trace)) {
return;
}

// Either the exception handler was not set or it could not handle the error,
// just log the exception.
FML_LOG(ERROR) << "Unhandled Exception: " << error << std::endl
<< stack_trace;
}

} // namespace blink
5 changes: 5 additions & 0 deletions lib/ui/ui_dart_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ class UIDartState : public tonic::DartState {

tonic::DartErrorHandleType GetLastError();

void ReportUnhandledException(const std::string& error,
const std::string& stack_trace);

template <class T>
static flow::SkiaGPUObject<T> CreateGPUObject(sk_sp<T> object) {
if (!object) {
Expand All @@ -77,6 +80,7 @@ class UIDartState : public tonic::DartState {
std::string advisory_script_uri,
std::string advisory_script_entrypoint,
std::string logger_prefix,
UnhandledExceptionCallback unhandled_exception_callback,
IsolateNameServer* isolate_name_server);

~UIDartState() override;
Expand All @@ -102,6 +106,7 @@ class UIDartState : public tonic::DartState {
std::string debug_name_;
std::unique_ptr<Window> window_;
tonic::DartMicrotaskQueue microtask_queue_;
UnhandledExceptionCallback unhandled_exception_callback_;
IsolateNameServer* isolate_name_server_;

void AddOrRemoveTaskObserver(bool add);
Expand Down
22 changes: 22 additions & 0 deletions lib/ui/window/window.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,27 @@ void SetIsolateDebugName(Dart_NativeArguments args) {
UIDartState::Current()->SetDebugName(name);
}

void ReportUnhandledException(Dart_NativeArguments args) {
Dart_Handle exception = nullptr;

auto error_name =
tonic::DartConverter<std::string>::FromArguments(args, 0, exception);
if (exception) {
Dart_ThrowException(exception);
return;
}

auto stack_trace =
tonic::DartConverter<std::string>::FromArguments(args, 1, exception);
if (exception) {
Dart_ThrowException(exception);
return;
}

UIDartState::Current()->ReportUnhandledException(std::move(error_name),
std::move(stack_trace));
}

Dart_Handle SendPlatformMessage(Dart_Handle window,
const std::string& name,
Dart_Handle callback,
Expand Down Expand Up @@ -318,6 +339,7 @@ void Window::RegisterNatives(tonic::DartLibraryNatives* natives) {
{"Window_render", Render, 2, true},
{"Window_updateSemantics", UpdateSemantics, 2, true},
{"Window_setIsolateDebugName", SetIsolateDebugName, 2, true},
{"Window_reportUnhandledException", ReportUnhandledException, 2, true},
});
}

Expand Down
77 changes: 38 additions & 39 deletions runtime/dart_isolate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "third_party/tonic/dart_message_handler.h"
#include "third_party/tonic/dart_state.h"
#include "third_party/tonic/file_loader/file_loader.h"
#include "third_party/tonic/logging/dart_invoke.h"
#include "third_party/tonic/scopes/dart_api_scope.h"
#include "third_party/tonic/scopes/dart_isolate_scope.h"

Expand Down Expand Up @@ -109,6 +110,7 @@ DartIsolate::DartIsolate(DartVM* vm,
advisory_script_uri,
advisory_script_entrypoint,
vm->GetSettings().log_tag,
vm->GetSettings().unhandled_exception_callback,
vm->GetIsolateNameServer()),
vm_(vm),
isolate_snapshot_(std::move(isolate_snapshot)),
Expand Down Expand Up @@ -391,33 +393,49 @@ bool DartIsolate::MarkIsolateRunnable() {
}

FML_WARN_UNUSED_RESULT
bool DartIsolate::Run(const std::string& entrypoint_name) {
TRACE_EVENT0("flutter", "DartIsolate::Run");
if (phase_ != Phase::Ready) {
static bool InvokeMainEntrypoint(Dart_Handle user_entrypoint_function) {
if (tonic::LogIfError(user_entrypoint_function)) {
FML_LOG(ERROR) << "Could not resolve main entrypoint function.";
return false;
}

tonic::DartState::Scope scope(this);
auto run_main_zoned_function =
Dart_GetField(Dart_LookupLibrary(tonic::ToDart("dart:ui")),
tonic::ToDart("_runMainZoned"));

Dart_Handle entrypoint =
Dart_GetField(Dart_RootLibrary(), tonic::ToDart(entrypoint_name.c_str()));
if (tonic::LogIfError(entrypoint)) {
auto start_main_isolate_function =
Dart_GetField(Dart_LookupLibrary(tonic::ToDart("dart:isolate")),
tonic::ToDart("_startMainIsolate"));

if (tonic::LogIfError(run_main_zoned_function) ||
tonic::LogIfError(start_main_isolate_function)) {
FML_LOG(ERROR) << "Could not resolve main entrypoint trampolines.";
return false;
}

Dart_Handle isolate_lib = Dart_LookupLibrary(tonic::ToDart("dart:isolate"));
if (tonic::LogIfError(isolate_lib)) {
if (tonic::LogIfError(tonic::DartInvoke(
run_main_zoned_function,
{start_main_isolate_function, user_entrypoint_function}))) {
FML_LOG(ERROR) << "Could not invoke the main entrypoint.";
return false;
}

Dart_Handle isolate_args[] = {
entrypoint,
Dart_Null(),
};
return true;
}

if (tonic::LogIfError(Dart_Invoke(
isolate_lib, tonic::ToDart("_startMainIsolate"),
sizeof(isolate_args) / sizeof(isolate_args[0]), isolate_args))) {
FML_WARN_UNUSED_RESULT
bool DartIsolate::Run(const std::string& entrypoint_name) {
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()));

if (!InvokeMainEntrypoint(user_entrypoint_function)) {
return false;
}

Expand All @@ -436,30 +454,11 @@ bool DartIsolate::RunFromLibrary(const std::string& library_name,

tonic::DartState::Scope scope(this);

Dart_Handle library = Dart_LookupLibrary(tonic::ToDart(library_name.c_str()));
if (tonic::LogIfError(library)) {
return false;
}

Dart_Handle entrypoint =
Dart_GetField(library, tonic::ToDart(entrypoint_name.c_str()));
if (tonic::LogIfError(entrypoint)) {
return false;
}

Dart_Handle isolate_lib = Dart_LookupLibrary(tonic::ToDart("dart:isolate"));
if (tonic::LogIfError(isolate_lib)) {
return false;
}

Dart_Handle isolate_args[] = {
entrypoint,
Dart_Null(),
};
auto user_entrypoint_function =
Dart_GetField(Dart_LookupLibrary(tonic::ToDart(library_name.c_str())),
tonic::ToDart(entrypoint_name.c_str()));

if (tonic::LogIfError(Dart_Invoke(
isolate_lib, tonic::ToDart("_startMainIsolate"),
sizeof(isolate_args) / sizeof(isolate_args[0]), isolate_args))) {
if (!InvokeMainEntrypoint(user_entrypoint_function)) {
return false;
}

Expand Down
1 change: 1 addition & 0 deletions runtime/dart_isolate.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class DartIsolate : public UIDartState {
DartVM* GetDartVM() const;

fml::RefPtr<DartSnapshot> GetIsolateSnapshot() const;

fml::RefPtr<DartSnapshot> GetSharedSnapshot() const;

std::weak_ptr<DartIsolate> GetWeakIsolatePtr();
Expand Down

0 comments on commit 25559ed

Please sign in to comment.