Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V8 inspector and error handling fixes #84

Merged
merged 7 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ include(FetchContent)
FetchContent_Declare(arcana.cpp
GIT_REPOSITORY https://github.com/microsoft/arcana.cpp.git
GIT_TAG f2757396e80bc4169f2ddb938ce25367a98ffdd0)
FetchContent_Declare(AndroidExtensions
GIT_REPOSITORY https://github.com/bghgary/AndroidExtensions.git
GIT_TAG 7d88a601fda9892791e7b4e994e375e049615688)
FetchContent_Declare(asio
GIT_REPOSITORY https://github.com/chriskohlhoff/asio.git
GIT_TAG f693a3eb7fe72a5f19b975289afc4f437d373d9c)
Expand Down
2 changes: 2 additions & 0 deletions Core/AppRuntime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ if(NAPI_JAVASCRIPT_ENGINE STREQUAL "V8" AND JSRUNTIMEHOST_CORE_APPRUNTIME_V8_INS

target_link_libraries(AppRuntime
PRIVATE v8inspector)

set_property(TARGET v8inspector PROPERTY FOLDER Dependencies)
endif()

set_property(TARGET AppRuntime PROPERTY FOLDER Core)
Expand Down
23 changes: 17 additions & 6 deletions Core/AppRuntime/Include/Babylon/AppRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,32 @@
namespace Babylon
{
class WorkQueue;

class AppRuntime final
{
public:
class Options
{
public:
// Optional handler for unhandled exceptions.
std::function<void(const Napi::Error&)> UnhandledExceptionHandler{DefaultUnhandledExceptionHandler};

// Waits for the debugger to be attached before the execution of any script. Only implemented for V8.
bool WaitForDebugger{false};
};

AppRuntime();
AppRuntime(std::function<void(const std::exception&)> unhandledExceptionHandler);
AppRuntime(Options options);
~AppRuntime();

void Suspend();
void Resume();

void Dispatch(Dispatchable<void(Napi::Env)> callback);

// Default unhandled exception handler that outputs the error message to the program output.
static void DefaultUnhandledExceptionHandler(const Napi::Error& error);

private:
// These three methods are the mechanism by which platform- and JavaScript-specific
// code can be "injected" into the execution of the JavaScript thread. These three
Expand All @@ -42,10 +56,7 @@ namespace Babylon
// extra logic around the invocation of a dispatched callback.
void Execute(Dispatchable<void()> callback);

static void DefaultUnhandledExceptionHandler(const std::exception& error);

std::unique_ptr<WorkQueue> m_workQueue{};
std::function<void(const std::exception&)> m_unhandledExceptionHandler{};
static std::string GetErrorInfos();
std::unique_ptr<WorkQueue> m_workQueue;
Options m_options;
};
}
60 changes: 8 additions & 52 deletions Core/AppRuntime/Source/AppRuntime.cpp
Original file line number Diff line number Diff line change
@@ -1,39 +1,17 @@
#include "AppRuntime.h"
#include "WorkQueue.h"
#include <sstream>

namespace {
std::string GetStringPropertyFromError(Napi::Error error, const char* propertyName)
{
Napi::Value value = error.Get(propertyName);
if (value.IsUndefined())
{
return "";
}
return value.ToString().Utf8Value();
}

int32_t GetNumberPropertyFromError(Napi::Error error, const char* propertyName)
{
Napi::Value value = error.Get(propertyName);
if (value.IsUndefined())
{
return -1;
}
return value.ToNumber().Int32Value();
}
}
#include <cassert>

namespace Babylon
{
AppRuntime::AppRuntime()
: AppRuntime{DefaultUnhandledExceptionHandler}
AppRuntime::AppRuntime() :
AppRuntime{{}}
{
}

AppRuntime::AppRuntime(std::function<void(const std::exception&)> unhandledExceptionHandler)
AppRuntime::AppRuntime(Options options)
: m_workQueue{std::make_unique<WorkQueue>([this] { RunPlatformTier(); })}
, m_unhandledExceptionHandler{unhandledExceptionHandler}
, m_options{std::move(options)}
{
Dispatch([this](Napi::Env env) {
JsRuntime::CreateForJavaScript(env, [this](auto func) { Dispatch(std::move(func)); });
Expand All @@ -59,29 +37,6 @@ namespace Babylon
m_workQueue->Resume();
}

std::string AppRuntime::GetErrorInfos()
{
std::ostringstream ss{};
try
{
throw;
}
catch (const Napi::Error& error)
{
std::string msg = error.Message();
std::string source = GetStringPropertyFromError(error, "source");
std::string url = GetStringPropertyFromError(error, "url");
int32_t line = GetNumberPropertyFromError(error, "line");
int32_t column = GetNumberPropertyFromError(error, "column");
int32_t length = GetNumberPropertyFromError(error, "length");
std::string stack = GetStringPropertyFromError(error, "stack");

ss << "Error on line " << line << " and column " << column
<< ": " << msg << ". Length: " << length << ". Source: " << source << ". URL: " << url << ". Stack:" << std::endl << stack << std::endl;
}
return ss.str();
}

void AppRuntime::Dispatch(Dispatchable<void(Napi::Env)> func)
{
m_workQueue->Append([this, func{std::move(func)}](Napi::Env env) mutable {
Expand All @@ -90,12 +45,13 @@ namespace Babylon
{
func(env);
}
catch (const std::exception& error)
catch (const Napi::Error& error)
{
m_unhandledExceptionHandler(error);
m_options.UnhandledExceptionHandler(error);
}
catch (...)
{
assert(false);
std::abort();
}
});
Expand Down
13 changes: 4 additions & 9 deletions Core/AppRuntime/Source/AppRuntime_Android.cpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
#include "AppRuntime.h"
#include <exception>
#include <sstream>
#include <android/log.h>

namespace Babylon
{
void AppRuntime::RunPlatformTier()
void AppRuntime::DefaultUnhandledExceptionHandler(const Napi::Error& error)
{
RunEnvironmentTier();
__android_log_print(ANDROID_LOG_ERROR, "BabylonNative", "[Uncaught Error] %s", error.Get("stack").As<Napi::String>().Utf8Value().data());
}

void AppRuntime::DefaultUnhandledExceptionHandler(const std::exception& error)
void AppRuntime::RunPlatformTier()
{
std::stringstream ss{};
ss << "Uncaught Error: " << error.what() << std::endl;
ss << GetErrorInfos() << std::endl;
__android_log_write(ANDROID_LOG_ERROR, "BabylonNative", ss.str().data());
RunEnvironmentTier();
}

void AppRuntime::Execute(Dispatchable<void()> callback)
Expand Down
17 changes: 6 additions & 11 deletions Core/AppRuntime/Source/AppRuntime_UWP.cpp
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
#include "AppRuntime.h"

#include <Windows.h>

#include <exception>
#include <sstream>

namespace Babylon
{
void AppRuntime::RunPlatformTier()
void AppRuntime::DefaultUnhandledExceptionHandler(const Napi::Error& error)
{
RunEnvironmentTier();
std::ostringstream ss{};
ss << "[Uncaught Error] " << error.Get("stack").As<Napi::String>().Utf8Value() << std::endl;
OutputDebugStringA(ss.str().data());
}

void AppRuntime::DefaultUnhandledExceptionHandler(const std::exception& error)
void AppRuntime::RunPlatformTier()
{
std::stringstream ss{};
ss << "Uncaught Error: " << error.what() << std::endl;
ss << GetErrorInfos() << std::endl;

OutputDebugStringA(ss.str().data());
RunEnvironmentTier();
}

void AppRuntime::Execute(Dispatchable<void()> callback)
Expand Down
15 changes: 4 additions & 11 deletions Core/AppRuntime/Source/AppRuntime_Unix.cpp
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
#include "WorkQueue.h"
#include "AppRuntime.h"
#include <exception>
#include <iostream>
#include <sstream>

namespace Babylon
{
void AppRuntime::RunPlatformTier()
void AppRuntime::DefaultUnhandledExceptionHandler(const Napi::Error& error)
{
RunEnvironmentTier();
std::cerr << "[Uncaught Error] " << error.Get("stack").As<Napi::String>().Utf8Value().data() << std::endl;
}

void AppRuntime::DefaultUnhandledExceptionHandler(const std::exception& error)
void AppRuntime::RunPlatformTier()
{
std::stringstream ss{};
ss << error.what() << std::endl;
ss << GetErrorInfos() << std::endl;

std::cerr << "Uncaught Error: " << ss.str().data() << std::endl;
RunEnvironmentTier();
}

void AppRuntime::Execute(Dispatchable<void()> callback)
Expand Down
25 changes: 18 additions & 7 deletions Core/AppRuntime/Source/AppRuntime_V8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,20 @@ namespace Babylon
v8::V8::DisposePlatform();
}

static Module& Initialize(const char* executablePath)
static void Initialize(const char* executablePath)
{
if (s_module == nullptr)
{
s_module = std::make_unique<Module>(executablePath);
}
}

static Module& Instance()
{
if (!s_module)
{
throw std::runtime_error{"Module not available"};
}

return *s_module;
}
Expand All @@ -66,10 +74,8 @@ namespace Babylon
void AppRuntime::RunEnvironmentTier(const char* executablePath)
{
// Create the isolate.
#ifdef ENABLE_V8_INSPECTOR
Module& module =
#endif
Module::Initialize(executablePath);

v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
Expand All @@ -84,14 +90,19 @@ namespace Babylon
Napi::Env env = Napi::Attach(context);

#ifdef ENABLE_V8_INSPECTOR
V8InspectorAgent agent{module.Platform(), isolate, context, "Babylon"};
agent.start(5643, "JsRuntimeHost");
V8InspectorAgent agent{Module::Instance().Platform(), isolate, context, "JsRuntimeHost"};
agent.Start(5643, "JsRuntimeHost");

if (m_options.WaitForDebugger)
{
agent.WaitForDebugger();
}
#endif

Run(env);

#ifdef ENABLE_V8_INSPECTOR
agent.stop();
agent.Stop();
#endif

Napi::Detach(env);
Expand Down
20 changes: 5 additions & 15 deletions Core/AppRuntime/Source/AppRuntime_Win32.cpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
#include "AppRuntime.h"

#include <Objbase.h>
#include <Windows.h>

#include <gsl/gsl>
#include <cassert>
#include <exception>
#include <sstream>

namespace Babylon
{
namespace
void AppRuntime::DefaultUnhandledExceptionHandler(const Napi::Error& error)
{
constexpr size_t FILENAME_BUFFER_SIZE = 1024;
std::ostringstream ss{};
ss << "[Uncaught Error] " << error.Get("stack").As<Napi::String>().Utf8Value() << std::endl;
OutputDebugStringA(ss.str().data());
}

void AppRuntime::RunPlatformTier()
Expand All @@ -22,22 +21,13 @@ namespace Babylon
_CRT_UNUSED(hr);
auto coInitScopeGuard = gsl::finally([] { CoUninitialize(); });

char filename[FILENAME_BUFFER_SIZE];
char filename[1024];
auto result = GetModuleFileNameA(nullptr, filename, static_cast<DWORD>(std::size(filename)));
assert(result != 0);
(void)result;
RunEnvironmentTier(filename);
}

void AppRuntime::DefaultUnhandledExceptionHandler(const std::exception& error)
{
std::stringstream ss{};
ss << "Uncaught Error: " << error.what() << std::endl;
ss << GetErrorInfos() << std::endl;

OutputDebugStringA(ss.str().data());
}

void AppRuntime::Execute(Dispatchable<void()> callback)
{
callback();
Expand Down
14 changes: 4 additions & 10 deletions Core/AppRuntime/Source/AppRuntime_iOS.mm
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
#include "AppRuntime.h"
#include <exception>
#include <sstream>
#import <Foundation/NSObjCRuntime.h>

namespace Babylon
{
void AppRuntime::RunPlatformTier()
void AppRuntime::DefaultUnhandledExceptionHandler(const Napi::Error& error)
{
RunEnvironmentTier();
NSLog(@"[Uncaught Error] %s", error.Get("stack").As<Napi::String>().Utf8Value().data());
}

void AppRuntime::DefaultUnhandledExceptionHandler(const std::exception& error)
void AppRuntime::RunPlatformTier()
{
std::stringstream ss{};
ss << error.what() << std::endl;
ss << GetErrorInfos() << std::endl;

NSLog(@"Uncaught Error: %s", ss.str().data());
RunEnvironmentTier();
}

void AppRuntime::Execute(Dispatchable<void()> callback)
Expand Down
14 changes: 4 additions & 10 deletions Core/AppRuntime/Source/AppRuntime_macOS.mm
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
#include "AppRuntime.h"
#include <exception>
#include <sstream>
#import <Foundation/NSObjCRuntime.h>

namespace Babylon
{
void AppRuntime::RunPlatformTier()
void AppRuntime::DefaultUnhandledExceptionHandler(const Napi::Error& error)
{
RunEnvironmentTier();
NSLog(@"[Uncaught Error] %s", error.Get("stack").As<Napi::String>().Utf8Value().data());
}

void AppRuntime::DefaultUnhandledExceptionHandler(const std::exception& error)
void AppRuntime::RunPlatformTier()
{
std::stringstream ss{};
ss << error.what() << std::endl;
ss << GetErrorInfos() << std::endl;

NSLog(@"Uncaught Error: %s", ss.str().data());
RunEnvironmentTier();
}

void AppRuntime::Execute(Dispatchable<void()> callback)
Expand Down
Loading
Loading