Skip to content

Commit 2cde720

Browse files
Save line/column/stack trace information about exceptions (#69)
fixes #42 --------- Co-authored-by: Cedric Guillemet <[email protected]>
1 parent 02055e8 commit 2cde720

File tree

9 files changed

+126
-6
lines changed

9 files changed

+126
-6
lines changed

Core/AppRuntime/Include/Babylon/AppRuntime.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,6 @@ namespace Babylon
4646

4747
std::unique_ptr<WorkQueue> m_workQueue{};
4848
std::function<void(const std::exception&)> m_unhandledExceptionHandler{};
49+
static std::string GetErrorInfos();
4950
};
5051
}

Core/AppRuntime/Source/AppRuntime.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
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+
}
326

427
namespace Babylon
528
{
@@ -36,6 +59,29 @@ namespace Babylon
3659
m_workQueue->Resume();
3760
}
3861

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+
3985
void AppRuntime::Dispatch(Dispatchable<void(Napi::Env)> func)
4086
{
4187
m_workQueue->Append([this, func{std::move(func)}](Napi::Env env) mutable {

Core/AppRuntime/Source/AppRuntime_Android.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ namespace Babylon
1414
{
1515
std::stringstream ss{};
1616
ss << "Uncaught Error: " << error.what() << std::endl;
17+
ss << GetErrorInfos() << std::endl;
1718
__android_log_write(ANDROID_LOG_ERROR, "BabylonNative", ss.str().data());
1819
}
1920

Core/AppRuntime/Source/AppRuntime_UWP.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ namespace Babylon
1616
{
1717
std::stringstream ss{};
1818
ss << "Uncaught Error: " << error.what() << std::endl;
19+
ss << GetErrorInfos() << std::endl;
20+
1921
OutputDebugStringA(ss.str().data());
2022
}
2123

Core/AppRuntime/Source/AppRuntime_Unix.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "AppRuntime.h"
33
#include <exception>
44
#include <iostream>
5+
#include <sstream>
56

67
namespace Babylon
78
{
@@ -12,7 +13,11 @@ namespace Babylon
1213

1314
void AppRuntime::DefaultUnhandledExceptionHandler(const std::exception& error)
1415
{
15-
std::cerr << "Uncaught Error: " << error.what() << std::endl;
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;
1621
}
1722

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

Core/AppRuntime/Source/AppRuntime_Win32.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ namespace Babylon
3333
{
3434
std::stringstream ss{};
3535
ss << "Uncaught Error: " << error.what() << std::endl;
36+
ss << GetErrorInfos() << std::endl;
37+
3638
OutputDebugStringA(ss.str().data());
3739
}
3840

Core/AppRuntime/Source/AppRuntime_iOS.mm

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

66
namespace Babylon
@@ -12,7 +12,11 @@
1212

1313
void AppRuntime::DefaultUnhandledExceptionHandler(const std::exception& error)
1414
{
15-
NSLog(@"Uncaught Error: %s", error.what());
15+
std::stringstream ss{};
16+
ss << error.what() << std::endl;
17+
ss << GetErrorInfos() << std::endl;
18+
19+
NSLog(@"Uncaught Error: %s", ss.str().data());
1620
}
1721

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

Core/AppRuntime/Source/AppRuntime_macOS.mm

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

66
namespace Babylon
@@ -12,7 +12,11 @@
1212

1313
void AppRuntime::DefaultUnhandledExceptionHandler(const std::exception& error)
1414
{
15-
NSLog(@"Uncaught Error: %s", error.what());
15+
std::stringstream ss{};
16+
ss << error.what() << std::endl;
17+
ss << GetErrorInfos() << std::endl;
18+
19+
NSLog(@"Uncaught Error: %s", ss.str().data());
1620
}
1721

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

Core/Node-API/Source/js_native_api_v8.h

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,13 +404,68 @@ class TryCatch : public v8::TryCatch {
404404
explicit TryCatch(napi_env env) : v8::TryCatch(env->isolate), _env(env) {}
405405

406406
~TryCatch() {
407+
// [BABYLON-NATIVE-ADDITION]
407408
if (HasCaught()) {
408-
_env->last_exception.Reset(_env->isolate, Exception());
409+
v8::Local<v8::Value> exception = Exception();
410+
EnhanceV8Exception(exception);
411+
_env->last_exception.Reset(_env->isolate, exception);
409412
}
413+
// [BABYLON-NATIVE-ADDITION]
410414
}
411415

412416
private:
413417
napi_env _env;
418+
419+
// [BABYLON-NATIVE-ADDITION]
420+
void EnhanceV8Exception(v8::Local<v8::Value> exception) {
421+
v8::Local<v8::Object> exception_obj = exception.As<v8::Object>();
422+
423+
v8::Isolate* isolate = _env->isolate;
424+
425+
v8::Local<v8::Message> message = Message();
426+
if (!message.IsEmpty()) {
427+
v8::Local<v8::Context> context(isolate->GetCurrentContext());
428+
429+
v8::ScriptOrigin script_origin = message->GetScriptOrigin();
430+
v8::Local<v8::Value> filename = script_origin.ResourceName();
431+
432+
setLocalValueAsProperty(context, exception_obj, filename, "url");
433+
434+
int column_start = message->GetStartColumn();
435+
int column_end = message->GetEndColumn();
436+
int length = column_end - column_start;
437+
setIntAsProperty(isolate, context, exception_obj, message->GetLineNumber(context).ToChecked(), "line");
438+
setIntAsProperty(isolate, context, exception_obj, column_start, "column");
439+
setIntAsProperty(isolate, context, exception_obj, length, "length");
440+
441+
v8::Local<v8::String> sourceLine = message->GetSourceLine(context).ToLocalChecked();
442+
setLocalValueAsProperty(context, exception_obj, sourceLine, "source");
443+
444+
v8::Local<v8::Value> stack_trace_string;
445+
if (StackTrace(context).ToLocal(&stack_trace_string) &&
446+
stack_trace_string->IsString() &&
447+
stack_trace_string.As<v8::String>()->Length() > 0) {
448+
setLocalValueAsProperty(context, exception_obj, stack_trace_string, "stack");
449+
}
450+
}
451+
}
452+
453+
// [BABYLON-NATIVE-ADDITION]
454+
void setIntAsProperty(v8::Isolate* isolate, v8::Local<v8::Context> context, v8::Local<v8::Object> obj, int value, const char* value_property) {
455+
v8::Local<v8::Number> v8_number = v8::Number::New(isolate, value);
456+
setLocalValueAsProperty(context, obj, v8_number, value_property);
457+
}
458+
459+
// [BABYLON-NATIVE-ADDITION]
460+
void setLocalValueAsProperty(v8::Local<v8::Context> context, v8::Local<v8::Object> obj, v8::Local<v8::Value> value, const char* value_property) {
461+
v8::Local<v8::String> key = createV8String(value_property);
462+
obj->Set(context, key, value);
463+
}
464+
465+
// [BABYLON-NATIVE-ADDITION]
466+
v8::Local<v8::String> createV8String(const char* text) {
467+
return v8::String::NewFromUtf8(_env->isolate, text).ToLocalChecked();
468+
}
414469
};
415470

416471
// Ownership of a reference.

0 commit comments

Comments
 (0)