Skip to content

Commit b7f8742

Browse files
committed
feat: add v8 exception handling
1 parent 1d454b7 commit b7f8742

File tree

6 files changed

+97
-96
lines changed

6 files changed

+97
-96
lines changed

src/loader/JavaScriptPluginLoader.cc

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "v8-isolate.h"
88
#include "v8-local-handle.h"
99
#include "v8-locker.h"
10+
#include "v8/V8Scope.h"
1011
#include <cstddef>
1112
#include <filesystem>
1213
#include <memory>
@@ -44,11 +45,7 @@ endstone::Plugin* JavaScriptPluginLoader::loadPlugin(std::string file) {
4445
{
4546
auto isolate = wrapper->isolate();
4647

47-
v8::Locker locker{isolate};
48-
v8::Isolate::Scope isolate_scope{isolate};
49-
v8::HandleScope handle_scope{isolate};
50-
v8::Local<v8::Context> context = wrapper->context();
51-
v8::Context::Scope context_scope{context};
48+
EnterV8Scope scope{isolate, wrapper->context()};
5249

5350
if (esm) {
5451
size_t max = 12; // 一般情况下, ESM 模块插件在第4个事件循环就会执行全局完全局代码

src/manager/NodeManager.cc

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#include "ObjectMapper.h"
33
#include "manager/RegisterNativeClasses.h"
44
#include "v8-isolate.h"
5+
#include "v8/V8Exception.h"
6+
#include "v8/V8Scope.h"
57
#pragma warning(disable : 4996)
68
#include "Entry.h"
79
#include "manager/NodeManager.h"
@@ -125,15 +127,14 @@ void NodeManager::initUvLoopThread() {
125127
continue;
126128
}
127129

130+
EnterV8Scope scope(wrapper->isolate(), wrapper->context());
128131
try {
129-
v8::Locker locker(wrapper->isolate());
130-
v8::Isolate::Scope isolate_scope(wrapper->isolate());
131-
v8::HandleScope handle_scope(wrapper->isolate());
132-
v8::Context::Scope context_scope(wrapper->context());
133-
v8::TryCatch vtry{wrapper->isolate()};
134-
132+
v8::TryCatch vtry{wrapper->isolate()};
135133
uv_run(wrapper->mEnvSetup->event_loop(), UV_RUN_NOWAIT);
136-
} catch (...) {};
134+
v8_exception::checkTryCatch(vtry);
135+
} catch (v8_exception const& except) {
136+
// TODO: handle v8 exception
137+
};
137138
}
138139
} catch (...) {};
139140
}
@@ -178,14 +179,6 @@ EngineWrapper* NodeManager::newScriptEngine() {
178179
auto context = envSetup->context();
179180
v8::Context::Scope contextScope(context);
180181

181-
context->Global()
182-
->Set(
183-
context,
184-
v8::String::NewFromUtf8(isolate, "__ENGINE_ID__").ToLocalChecked(),
185-
v8::Number::New(isolate, static_cast<double>(id))
186-
)
187-
.Check();
188-
189182
auto ptr = std::make_unique<EngineWrapper>(id, std::move(envSetup));
190183

191184
auto mapper = new puerts::FCppObjectMapper();
@@ -383,37 +376,33 @@ bool NodeManager::loadFile(EngineWrapper* wrapper, fs::path const& path, bool es
383376
auto* isolate = wrapper->isolate();
384377

385378
{
386-
v8::Locker lock(isolate);
387-
v8::Isolate::Scope isolate_scope(isolate);
388-
v8::HandleScope handle_scope(isolate);
389-
v8::Context::Scope context_scope(wrapper->context());
390-
v8::TryCatch vtry(isolate);
379+
EnterV8Scope enter{isolate, wrapper->context()};
380+
v8::TryCatch vtry(isolate);
391381
node::SetProcessExitHandler(env, [id{wrapper->mID}, isolate](node::Environment*, int exit_code) {
392382
isolate->Exit();
393383
Entry::getInstance()->getLogger().debug("Node.js process exit with code: {}, id: {}", exit_code, id);
394384
if (exit_code == 0) NodeManager::getInstance().destroyEngine(id);
395385
});
386+
v8_exception::checkTryCatch(vtry);
396387
}
397388

398389
{
399-
v8::Locker lock(isolate);
400-
v8::Isolate::Scope isolate_scope(isolate);
401-
v8::HandleScope handle_scope(isolate);
402-
v8::Context::Scope context_scope(wrapper->context());
403-
v8::TryCatch vtry(isolate);
390+
EnterV8Scope enter{isolate, wrapper->context()};
391+
v8::TryCatch vtry(isolate);
392+
404393
v8::MaybeLocal<v8::Value> loadValue = node::LoadEnvironment(env, loader);
405-
if (loadValue.IsEmpty() || vtry.HasCaught()) {
406-
v8::String::Utf8Value error(isolate, vtry.Exception());
407-
v8::String::Utf8Value stack(isolate, vtry.StackTrace(wrapper->mEnvSetup->context()).ToLocalChecked());
408-
Entry::getInstance()->getLogger().error("{}\n{}", *error, *stack);
409-
// ExitEngineScope exit;
410-
// isolate->Exit();
394+
if (loadValue.IsEmpty()) {
395+
v8_exception::checkTryCatch(vtry);
411396
return false;
412397
}
398+
v8_exception::checkTryCatch(vtry);
413399
}
414400

415401
wrapper->mIsRunning = true;
416402
return true;
403+
} catch (v8_exception const& exc) {
404+
// TODO: handle v8 exception
405+
return false;
417406
} catch (...) {
418407
return false;
419408
}

src/manager/RegisterNativeClasses.h

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "manager/EngineWrapper.h"
88
#include "pesapi.h"
99
#include "utils/Using.h"
10+
#include "v8-context.h"
1011
#include "v8-external.h"
1112
#include "v8-function-callback.h"
1213
#include "v8-isolate.h"
@@ -44,18 +45,12 @@ namespace jse {
4445

4546
inline void RegisterGlobalFunc(EngineWrapper* wrapper) {
4647
auto isolate = wrapper->isolate();
47-
48-
v8::Locker lock(isolate);
49-
v8::Isolate::Scope isolate_scope(isolate);
50-
v8::HandleScope handle_scope(isolate);
51-
v8::Local<v8::Context> context = wrapper->context();
52-
v8::Context::Scope context_scope(context);
53-
54-
auto global = context->Global();
48+
auto ctx = wrapper->context();
49+
auto global = ctx->Global();
5550

5651
global
5752
->Set(
58-
context,
53+
ctx,
5954
v8::String::NewFromUtf8(isolate, "loadNativeClass").ToLocalChecked(),
6055
v8::FunctionTemplate::New(
6156
isolate,
@@ -66,14 +61,14 @@ inline void RegisterGlobalFunc(EngineWrapper* wrapper) {
6661
},
6762
v8::External::New(isolate, wrapper->mMapper)
6863
)
69-
->GetFunction(context)
64+
->GetFunction(ctx)
7065
.ToLocalChecked()
7166
)
7267
.Check();
7368

7469
global
7570
->Set(
76-
context,
71+
ctx,
7772
v8::String::NewFromUtf8(isolate, "__declaration__").ToLocalChecked(),
7873
v8::FunctionTemplate::New(
7974
isolate,
@@ -92,23 +87,21 @@ inline void RegisterGlobalFunc(EngineWrapper* wrapper) {
9287
);
9388
}
9489
)
95-
->GetFunction(context)
90+
->GetFunction(ctx)
9691
.ToLocalChecked()
9792
)
9893
.Check();
94+
95+
global
96+
->Set(
97+
ctx,
98+
v8::String::NewFromUtf8(isolate, "__ENGINE_ID__").ToLocalChecked(),
99+
v8::Number::New(isolate, static_cast<double>(wrapper->mID))
100+
)
101+
.Check();
99102
}
100103

101104
inline void RegisterNativeClasses(EngineWrapper* wrapper) {
102-
auto isolate = wrapper->isolate();
103-
auto env = wrapper->env();
104-
105-
v8::Locker lock(isolate);
106-
v8::Isolate::Scope isolate_scope(isolate);
107-
v8::HandleScope handle_scope(isolate);
108-
v8::Local<v8::Context> context = wrapper->context();
109-
v8::Context::Scope context_scope(context);
110-
111-
112105
RegisterGlobalFunc(wrapper);
113106

114107
RegisterJSEAPI();

src/v8/V8Exception.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#pragma once
2+
#include "v8-context.h"
3+
#include "v8-exception.h"
4+
#include "v8-isolate.h"
5+
#include "v8-local-handle.h"
6+
#include "v8-locker.h"
7+
#include <exception>
8+
#include <string>
9+
10+
namespace jse {
11+
12+
13+
class v8_exception : public std::exception {
14+
v8::Isolate* isolate_;
15+
v8::Local<v8::Context> ctx_;
16+
std::string message_;
17+
v8::Local<v8::Value> exception_;
18+
19+
public:
20+
v8_exception(v8::Isolate* isolate, v8::Local<v8::Context> ctx, v8::Local<v8::Value> exception)
21+
: isolate_(isolate),
22+
ctx_(ctx),
23+
exception_(exception) {
24+
v8::TryCatch try_catch(isolate_);
25+
message_ = *v8::String::Utf8Value(isolate_, exception_);
26+
}
27+
28+
[[nodiscard]] const char* what() const noexcept override { return message_.c_str(); }
29+
30+
[[nodiscard]] v8::Local<v8::Value> exception() const noexcept { return exception_; }
31+
32+
[[nodiscard]] std::string message() const noexcept { return message_; }
33+
34+
[[nodiscard]] std::string stacktrace() const noexcept {
35+
v8::TryCatch try_catch(isolate_);
36+
auto maybe_stack = v8::TryCatch::StackTrace(ctx_, exception_);
37+
if (maybe_stack.IsEmpty()) {
38+
return "No stack trace available";
39+
}
40+
auto stack = maybe_stack.ToLocalChecked();
41+
return *v8::String::Utf8Value(isolate_, stack);
42+
}
43+
44+
45+
/**
46+
* @brief 检查 v8::TryCatch 是否有异常,如果有则抛出 v8_exception
47+
* @warning 必须在 v8 作用域内调用
48+
*/
49+
static void checkTryCatch(v8::TryCatch& vtry) {
50+
if (vtry.HasCaught()) {
51+
auto isolate = v8::Isolate::GetCurrent();
52+
auto ctx = isolate->GetCurrentContext();
53+
auto exception = vtry.Exception();
54+
throw v8_exception(isolate, ctx, exception);
55+
}
56+
}
57+
};
58+
59+
60+
} // namespace jse

src/v8/V8GlobalThis.h

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/v8/V8Scope.h

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44
#include "v8-isolate.h"
55
#include "v8-local-handle.h"
66
#include "v8-locker.h"
7-
#include <exception>
8-
#include <stdexcept>
9-
#include <string>
107

118

129
namespace jse {
@@ -61,13 +58,4 @@ class HandleV8Scope final {
6158
};
6259

6360

64-
inline void CheckV8Exception(v8::Isolate* isolate, v8::Local<v8::Context> ctx, v8::TryCatch& vtry) {
65-
if (vtry.HasCaught()) {
66-
v8::String::Utf8Value exception(isolate, vtry.Exception());
67-
v8::String::Utf8Value stack(isolate, vtry.StackTrace(ctx).ToLocalChecked());
68-
throw std::runtime_error(std::string(*exception) + "\n" + std::string(*stack));
69-
}
70-
}
71-
72-
7361
} // namespace jse

0 commit comments

Comments
 (0)