Skip to content

Commit 5f20827

Browse files
authored
V8 inspector and error handling fixes (#84)
- Remove dead code in V8 inspector - Fix race condition on V8 inspector shutdown - Add options struct to AppRuntime to support new WaitForDebugger flag - Use new WaitForDebugger flag in JavaScript tests - Remove newline from console log callback Fixes #21 Fixes #61
1 parent 2cde720 commit 5f20827

File tree

21 files changed

+266
-497
lines changed

21 files changed

+266
-497
lines changed

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ include(FetchContent)
1212
FetchContent_Declare(arcana.cpp
1313
GIT_REPOSITORY https://github.com/microsoft/arcana.cpp.git
1414
GIT_TAG f2757396e80bc4169f2ddb938ce25367a98ffdd0)
15+
FetchContent_Declare(AndroidExtensions
16+
GIT_REPOSITORY https://github.com/bghgary/AndroidExtensions.git
17+
GIT_TAG 7d88a601fda9892791e7b4e994e375e049615688)
1518
FetchContent_Declare(asio
1619
GIT_REPOSITORY https://github.com/chriskohlhoff/asio.git
1720
GIT_TAG f693a3eb7fe72a5f19b975289afc4f437d373d9c)

Core/AppRuntime/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ if(NAPI_JAVASCRIPT_ENGINE STREQUAL "V8" AND JSRUNTIMEHOST_CORE_APPRUNTIME_V8_INS
3535

3636
target_link_libraries(AppRuntime
3737
PRIVATE v8inspector)
38+
39+
set_property(TARGET v8inspector PROPERTY FOLDER Dependencies)
3840
endif()
3941

4042
set_property(TARGET AppRuntime PROPERTY FOLDER Core)

Core/AppRuntime/Include/Babylon/AppRuntime.h

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,32 @@
1111
namespace Babylon
1212
{
1313
class WorkQueue;
14+
1415
class AppRuntime final
1516
{
1617
public:
18+
class Options
19+
{
20+
public:
21+
// Optional handler for unhandled exceptions.
22+
std::function<void(const Napi::Error&)> UnhandledExceptionHandler{DefaultUnhandledExceptionHandler};
23+
24+
// Waits for the debugger to be attached before the execution of any script. Only implemented for V8.
25+
bool WaitForDebugger{false};
26+
};
27+
1728
AppRuntime();
18-
AppRuntime(std::function<void(const std::exception&)> unhandledExceptionHandler);
29+
AppRuntime(Options options);
1930
~AppRuntime();
2031

2132
void Suspend();
2233
void Resume();
2334

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

37+
// Default unhandled exception handler that outputs the error message to the program output.
38+
static void DefaultUnhandledExceptionHandler(const Napi::Error& error);
39+
2640
private:
2741
// These three methods are the mechanism by which platform- and JavaScript-specific
2842
// code can be "injected" into the execution of the JavaScript thread. These three
@@ -42,10 +56,7 @@ namespace Babylon
4256
// extra logic around the invocation of a dispatched callback.
4357
void Execute(Dispatchable<void()> callback);
4458

45-
static void DefaultUnhandledExceptionHandler(const std::exception& error);
46-
47-
std::unique_ptr<WorkQueue> m_workQueue{};
48-
std::function<void(const std::exception&)> m_unhandledExceptionHandler{};
49-
static std::string GetErrorInfos();
59+
std::unique_ptr<WorkQueue> m_workQueue;
60+
Options m_options;
5061
};
5162
}

Core/AppRuntime/Source/AppRuntime.cpp

Lines changed: 8 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,17 @@
11
#include "AppRuntime.h"
22
#include "WorkQueue.h"
3-
#include <sstream>
4-
5-
namespace {
6-
std::string GetStringPropertyFromError(Napi::Error error, const char* propertyName)
7-
{
8-
Napi::Value value = error.Get(propertyName);
9-
if (value.IsUndefined())
10-
{
11-
return "";
12-
}
13-
return value.ToString().Utf8Value();
14-
}
15-
16-
int32_t GetNumberPropertyFromError(Napi::Error error, const char* propertyName)
17-
{
18-
Napi::Value value = error.Get(propertyName);
19-
if (value.IsUndefined())
20-
{
21-
return -1;
22-
}
23-
return value.ToNumber().Int32Value();
24-
}
25-
}
3+
#include <cassert>
264

275
namespace Babylon
286
{
29-
AppRuntime::AppRuntime()
30-
: AppRuntime{DefaultUnhandledExceptionHandler}
7+
AppRuntime::AppRuntime() :
8+
AppRuntime{{}}
319
{
3210
}
3311

34-
AppRuntime::AppRuntime(std::function<void(const std::exception&)> unhandledExceptionHandler)
12+
AppRuntime::AppRuntime(Options options)
3513
: m_workQueue{std::make_unique<WorkQueue>([this] { RunPlatformTier(); })}
36-
, m_unhandledExceptionHandler{unhandledExceptionHandler}
14+
, m_options{std::move(options)}
3715
{
3816
Dispatch([this](Napi::Env env) {
3917
JsRuntime::CreateForJavaScript(env, [this](auto func) { Dispatch(std::move(func)); });
@@ -59,29 +37,6 @@ namespace Babylon
5937
m_workQueue->Resume();
6038
}
6139

62-
std::string AppRuntime::GetErrorInfos()
63-
{
64-
std::ostringstream ss{};
65-
try
66-
{
67-
throw;
68-
}
69-
catch (const Napi::Error& error)
70-
{
71-
std::string msg = error.Message();
72-
std::string source = GetStringPropertyFromError(error, "source");
73-
std::string url = GetStringPropertyFromError(error, "url");
74-
int32_t line = GetNumberPropertyFromError(error, "line");
75-
int32_t column = GetNumberPropertyFromError(error, "column");
76-
int32_t length = GetNumberPropertyFromError(error, "length");
77-
std::string stack = GetStringPropertyFromError(error, "stack");
78-
79-
ss << "Error on line " << line << " and column " << column
80-
<< ": " << msg << ". Length: " << length << ". Source: " << source << ". URL: " << url << ". Stack:" << std::endl << stack << std::endl;
81-
}
82-
return ss.str();
83-
}
84-
8540
void AppRuntime::Dispatch(Dispatchable<void(Napi::Env)> func)
8641
{
8742
m_workQueue->Append([this, func{std::move(func)}](Napi::Env env) mutable {
@@ -90,12 +45,13 @@ namespace Babylon
9045
{
9146
func(env);
9247
}
93-
catch (const std::exception& error)
48+
catch (const Napi::Error& error)
9449
{
95-
m_unhandledExceptionHandler(error);
50+
m_options.UnhandledExceptionHandler(error);
9651
}
9752
catch (...)
9853
{
54+
assert(false);
9955
std::abort();
10056
}
10157
});

Core/AppRuntime/Source/AppRuntime_Android.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
#include "AppRuntime.h"
2-
#include <exception>
3-
#include <sstream>
42
#include <android/log.h>
53

64
namespace Babylon
75
{
8-
void AppRuntime::RunPlatformTier()
6+
void AppRuntime::DefaultUnhandledExceptionHandler(const Napi::Error& error)
97
{
10-
RunEnvironmentTier();
8+
__android_log_print(ANDROID_LOG_ERROR, "BabylonNative", "[Uncaught Error] %s", error.Get("stack").As<Napi::String>().Utf8Value().data());
119
}
1210

13-
void AppRuntime::DefaultUnhandledExceptionHandler(const std::exception& error)
11+
void AppRuntime::RunPlatformTier()
1412
{
15-
std::stringstream ss{};
16-
ss << "Uncaught Error: " << error.what() << std::endl;
17-
ss << GetErrorInfos() << std::endl;
18-
__android_log_write(ANDROID_LOG_ERROR, "BabylonNative", ss.str().data());
13+
RunEnvironmentTier();
1914
}
2015

2116
void AppRuntime::Execute(Dispatchable<void()> callback)

Core/AppRuntime/Source/AppRuntime_UWP.cpp

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
11
#include "AppRuntime.h"
2-
32
#include <Windows.h>
4-
5-
#include <exception>
63
#include <sstream>
74

85
namespace Babylon
96
{
10-
void AppRuntime::RunPlatformTier()
7+
void AppRuntime::DefaultUnhandledExceptionHandler(const Napi::Error& error)
118
{
12-
RunEnvironmentTier();
9+
std::ostringstream ss{};
10+
ss << "[Uncaught Error] " << error.Get("stack").As<Napi::String>().Utf8Value() << std::endl;
11+
OutputDebugStringA(ss.str().data());
1312
}
1413

15-
void AppRuntime::DefaultUnhandledExceptionHandler(const std::exception& error)
14+
void AppRuntime::RunPlatformTier()
1615
{
17-
std::stringstream ss{};
18-
ss << "Uncaught Error: " << error.what() << std::endl;
19-
ss << GetErrorInfos() << std::endl;
20-
21-
OutputDebugStringA(ss.str().data());
16+
RunEnvironmentTier();
2217
}
2318

2419
void AppRuntime::Execute(Dispatchable<void()> callback)

Core/AppRuntime/Source/AppRuntime_Unix.cpp

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,16 @@
1-
#include "WorkQueue.h"
21
#include "AppRuntime.h"
3-
#include <exception>
42
#include <iostream>
5-
#include <sstream>
63

74
namespace Babylon
85
{
9-
void AppRuntime::RunPlatformTier()
6+
void AppRuntime::DefaultUnhandledExceptionHandler(const Napi::Error& error)
107
{
11-
RunEnvironmentTier();
8+
std::cerr << "[Uncaught Error] " << error.Get("stack").As<Napi::String>().Utf8Value().data() << std::endl;
129
}
1310

14-
void AppRuntime::DefaultUnhandledExceptionHandler(const std::exception& error)
11+
void AppRuntime::RunPlatformTier()
1512
{
16-
std::stringstream ss{};
17-
ss << error.what() << std::endl;
18-
ss << GetErrorInfos() << std::endl;
19-
20-
std::cerr << "Uncaught Error: " << ss.str().data() << std::endl;
13+
RunEnvironmentTier();
2114
}
2215

2316
void AppRuntime::Execute(Dispatchable<void()> callback)

Core/AppRuntime/Source/AppRuntime_V8.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,20 @@ namespace Babylon
3939
v8::V8::DisposePlatform();
4040
}
4141

42-
static Module& Initialize(const char* executablePath)
42+
static void Initialize(const char* executablePath)
4343
{
4444
if (s_module == nullptr)
4545
{
4646
s_module = std::make_unique<Module>(executablePath);
4747
}
48+
}
49+
50+
static Module& Instance()
51+
{
52+
if (!s_module)
53+
{
54+
throw std::runtime_error{"Module not available"};
55+
}
4856

4957
return *s_module;
5058
}
@@ -66,10 +74,8 @@ namespace Babylon
6674
void AppRuntime::RunEnvironmentTier(const char* executablePath)
6775
{
6876
// Create the isolate.
69-
#ifdef ENABLE_V8_INSPECTOR
70-
Module& module =
71-
#endif
7277
Module::Initialize(executablePath);
78+
7379
v8::Isolate::CreateParams create_params;
7480
create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
7581
v8::Isolate* isolate = v8::Isolate::New(create_params);
@@ -84,14 +90,19 @@ namespace Babylon
8490
Napi::Env env = Napi::Attach(context);
8591

8692
#ifdef ENABLE_V8_INSPECTOR
87-
V8InspectorAgent agent{module.Platform(), isolate, context, "Babylon"};
88-
agent.start(5643, "JsRuntimeHost");
93+
V8InspectorAgent agent{Module::Instance().Platform(), isolate, context, "JsRuntimeHost"};
94+
agent.Start(5643, "JsRuntimeHost");
95+
96+
if (m_options.WaitForDebugger)
97+
{
98+
agent.WaitForDebugger();
99+
}
89100
#endif
90101

91102
Run(env);
92103

93104
#ifdef ENABLE_V8_INSPECTOR
94-
agent.stop();
105+
agent.Stop();
95106
#endif
96107

97108
Napi::Detach(env);

Core/AppRuntime/Source/AppRuntime_Win32.cpp

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
#include "AppRuntime.h"
2-
32
#include <Objbase.h>
43
#include <Windows.h>
5-
64
#include <gsl/gsl>
75
#include <cassert>
8-
#include <exception>
96
#include <sstream>
107

118
namespace Babylon
129
{
13-
namespace
10+
void AppRuntime::DefaultUnhandledExceptionHandler(const Napi::Error& error)
1411
{
15-
constexpr size_t FILENAME_BUFFER_SIZE = 1024;
12+
std::ostringstream ss{};
13+
ss << "[Uncaught Error] " << error.Get("stack").As<Napi::String>().Utf8Value() << std::endl;
14+
OutputDebugStringA(ss.str().data());
1615
}
1716

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

25-
char filename[FILENAME_BUFFER_SIZE];
24+
char filename[1024];
2625
auto result = GetModuleFileNameA(nullptr, filename, static_cast<DWORD>(std::size(filename)));
2726
assert(result != 0);
2827
(void)result;
2928
RunEnvironmentTier(filename);
3029
}
3130

32-
void AppRuntime::DefaultUnhandledExceptionHandler(const std::exception& error)
33-
{
34-
std::stringstream ss{};
35-
ss << "Uncaught Error: " << error.what() << std::endl;
36-
ss << GetErrorInfos() << std::endl;
37-
38-
OutputDebugStringA(ss.str().data());
39-
}
40-
4131
void AppRuntime::Execute(Dispatchable<void()> callback)
4232
{
4333
callback();

Core/AppRuntime/Source/AppRuntime_iOS.mm

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
11
#include "AppRuntime.h"
2-
#include <exception>
3-
#include <sstream>
42
#import <Foundation/NSObjCRuntime.h>
53

64
namespace Babylon
75
{
8-
void AppRuntime::RunPlatformTier()
6+
void AppRuntime::DefaultUnhandledExceptionHandler(const Napi::Error& error)
97
{
10-
RunEnvironmentTier();
8+
NSLog(@"[Uncaught Error] %s", error.Get("stack").As<Napi::String>().Utf8Value().data());
119
}
1210

13-
void AppRuntime::DefaultUnhandledExceptionHandler(const std::exception& error)
11+
void AppRuntime::RunPlatformTier()
1412
{
15-
std::stringstream ss{};
16-
ss << error.what() << std::endl;
17-
ss << GetErrorInfos() << std::endl;
18-
19-
NSLog(@"Uncaught Error: %s", ss.str().data());
13+
RunEnvironmentTier();
2014
}
2115

2216
void AppRuntime::Execute(Dispatchable<void()> callback)

0 commit comments

Comments
 (0)