@nocobase/plugin-workflow-javascript 2.1.0-beta.10 → 2.1.0-beta.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/externalVersion.js +4 -4
- package/dist/node_modules/isolated-vm/.clang-tidy +13 -0
- package/dist/node_modules/isolated-vm/.dockerignore +9 -0
- package/dist/node_modules/isolated-vm/Dockerfile.alpine +9 -0
- package/dist/node_modules/isolated-vm/Dockerfile.debian +12 -0
- package/dist/node_modules/isolated-vm/LICENSE +13 -0
- package/dist/node_modules/isolated-vm/binding.gyp +120 -0
- package/dist/node_modules/isolated-vm/include.js +3 -0
- package/dist/node_modules/isolated-vm/inspector-example.js +59 -0
- package/dist/node_modules/isolated-vm/isolated-vm.d.ts +820 -0
- package/dist/node_modules/isolated-vm/isolated-vm.js +1 -0
- package/dist/node_modules/isolated-vm/native-example/binding.gyp +23 -0
- package/dist/node_modules/isolated-vm/native-example/example.cc +61 -0
- package/dist/node_modules/isolated-vm/native-example/package.json +13 -0
- package/dist/node_modules/isolated-vm/native-example/usage.js +35 -0
- package/dist/node_modules/isolated-vm/out/isolated_vm.node +0 -0
- package/dist/node_modules/isolated-vm/package.json +1 -0
- package/dist/node_modules/isolated-vm/src/external_copy/error.h +33 -0
- package/dist/node_modules/isolated-vm/src/external_copy/external_copy.cc +509 -0
- package/dist/node_modules/isolated-vm/src/external_copy/external_copy.h +117 -0
- package/dist/node_modules/isolated-vm/src/external_copy/serializer.cc +85 -0
- package/dist/node_modules/isolated-vm/src/external_copy/serializer.h +136 -0
- package/dist/node_modules/isolated-vm/src/external_copy/serializer_nortti.cc +73 -0
- package/dist/node_modules/isolated-vm/src/external_copy/string.cc +124 -0
- package/dist/node_modules/isolated-vm/src/external_copy/string.h +28 -0
- package/dist/node_modules/isolated-vm/src/isolate/allocator.h +32 -0
- package/dist/node_modules/isolated-vm/src/isolate/allocator_nortti.cc +142 -0
- package/dist/node_modules/isolated-vm/src/isolate/class_handle.h +334 -0
- package/dist/node_modules/isolated-vm/src/isolate/cpu_profile_manager.cc +220 -0
- package/dist/node_modules/isolated-vm/src/isolate/cpu_profile_manager.h +100 -0
- package/dist/node_modules/isolated-vm/src/isolate/environment.cc +626 -0
- package/dist/node_modules/isolated-vm/src/isolate/environment.h +381 -0
- package/dist/node_modules/isolated-vm/src/isolate/executor.cc +198 -0
- package/dist/node_modules/isolated-vm/src/isolate/executor.h +183 -0
- package/dist/node_modules/isolated-vm/src/isolate/external.h +64 -0
- package/dist/node_modules/isolated-vm/src/isolate/functor_runners.h +97 -0
- package/dist/node_modules/isolated-vm/src/isolate/generic/array.h +145 -0
- package/dist/node_modules/isolated-vm/src/isolate/generic/callbacks.h +272 -0
- package/dist/node_modules/isolated-vm/src/isolate/generic/error.h +140 -0
- package/dist/node_modules/isolated-vm/src/isolate/generic/extract_params.h +145 -0
- package/dist/node_modules/isolated-vm/src/isolate/generic/handle_cast.h +257 -0
- package/dist/node_modules/isolated-vm/src/isolate/generic/read_option.h +47 -0
- package/dist/node_modules/isolated-vm/src/isolate/holder.cc +88 -0
- package/dist/node_modules/isolated-vm/src/isolate/holder.h +63 -0
- package/dist/node_modules/isolated-vm/src/isolate/inspector.cc +200 -0
- package/dist/node_modules/isolated-vm/src/isolate/inspector.h +70 -0
- package/dist/node_modules/isolated-vm/src/isolate/node_wrapper.h +15 -0
- package/dist/node_modules/isolated-vm/src/isolate/platform_delegate.cc +22 -0
- package/dist/node_modules/isolated-vm/src/isolate/platform_delegate.h +46 -0
- package/dist/node_modules/isolated-vm/src/isolate/remote_handle.h +164 -0
- package/dist/node_modules/isolated-vm/src/isolate/run_with_timeout.h +171 -0
- package/dist/node_modules/isolated-vm/src/isolate/runnable.h +29 -0
- package/dist/node_modules/isolated-vm/src/isolate/scheduler.cc +191 -0
- package/dist/node_modules/isolated-vm/src/isolate/scheduler.h +165 -0
- package/dist/node_modules/isolated-vm/src/isolate/specific.h +35 -0
- package/dist/node_modules/isolated-vm/src/isolate/stack_trace.cc +219 -0
- package/dist/node_modules/isolated-vm/src/isolate/stack_trace.h +24 -0
- package/dist/node_modules/isolated-vm/src/isolate/strings.h +127 -0
- package/dist/node_modules/isolated-vm/src/isolate/three_phase_task.cc +385 -0
- package/dist/node_modules/isolated-vm/src/isolate/three_phase_task.h +136 -0
- package/dist/node_modules/isolated-vm/src/isolate/transferable.h +15 -0
- package/dist/node_modules/isolated-vm/src/isolate/util.h +45 -0
- package/dist/node_modules/isolated-vm/src/isolate/v8_inspector_wrapper.h +12 -0
- package/dist/node_modules/isolated-vm/src/isolate/v8_version.h +12 -0
- package/dist/node_modules/isolated-vm/src/isolated_vm.h +71 -0
- package/dist/node_modules/isolated-vm/src/lib/covariant.h +50 -0
- package/dist/node_modules/isolated-vm/src/lib/lockable.h +178 -0
- package/dist/node_modules/isolated-vm/src/lib/suspend.h +106 -0
- package/dist/node_modules/isolated-vm/src/lib/thread_pool.cc +98 -0
- package/dist/node_modules/isolated-vm/src/lib/thread_pool.h +45 -0
- package/dist/node_modules/isolated-vm/src/lib/timer.cc +233 -0
- package/dist/node_modules/isolated-vm/src/lib/timer.h +36 -0
- package/dist/node_modules/isolated-vm/src/module/callback.cc +151 -0
- package/dist/node_modules/isolated-vm/src/module/callback.h +64 -0
- package/dist/node_modules/isolated-vm/src/module/context_handle.cc +241 -0
- package/dist/node_modules/isolated-vm/src/module/context_handle.h +35 -0
- package/dist/node_modules/isolated-vm/src/module/evaluation.cc +109 -0
- package/dist/node_modules/isolated-vm/src/module/evaluation.h +99 -0
- package/dist/node_modules/isolated-vm/src/module/external_copy_handle.cc +119 -0
- package/dist/node_modules/isolated-vm/src/module/external_copy_handle.h +64 -0
- package/dist/node_modules/isolated-vm/src/module/isolate.cc +136 -0
- package/dist/node_modules/isolated-vm/src/module/isolate_handle.cc +611 -0
- package/dist/node_modules/isolated-vm/src/module/isolate_handle.h +47 -0
- package/dist/node_modules/isolated-vm/src/module/lib_handle.cc +77 -0
- package/dist/node_modules/isolated-vm/src/module/lib_handle.h +28 -0
- package/dist/node_modules/isolated-vm/src/module/module_handle.cc +475 -0
- package/dist/node_modules/isolated-vm/src/module/module_handle.h +68 -0
- package/dist/node_modules/isolated-vm/src/module/native_module_handle.cc +104 -0
- package/dist/node_modules/isolated-vm/src/module/native_module_handle.h +49 -0
- package/dist/node_modules/isolated-vm/src/module/reference_handle.cc +636 -0
- package/dist/node_modules/isolated-vm/src/module/reference_handle.h +106 -0
- package/dist/node_modules/isolated-vm/src/module/script_handle.cc +107 -0
- package/dist/node_modules/isolated-vm/src/module/script_handle.h +37 -0
- package/dist/node_modules/isolated-vm/src/module/session_handle.cc +173 -0
- package/dist/node_modules/isolated-vm/src/module/session_handle.h +31 -0
- package/dist/node_modules/isolated-vm/src/module/transferable.cc +268 -0
- package/dist/node_modules/isolated-vm/src/module/transferable.h +42 -0
- package/dist/node_modules/isolated-vm/vendor/v8_inspector/nodejs_v18.0.0.h +360 -0
- package/dist/node_modules/isolated-vm/vendor/v8_inspector/nodejs_v18.3.0.h +376 -0
- package/dist/node_modules/isolated-vm/vendor/v8_inspector/nodejs_v20.0.0.h +397 -0
- package/dist/node_modules/isolated-vm/vendor/v8_inspector/nodejs_v22.0.0.h +419 -0
- package/dist/node_modules/winston-transport/package.json +1 -1
- package/dist/server/IsolatedVm.js +75 -0
- package/dist/server/ScriptInstruction.d.ts +6 -0
- package/dist/server/ScriptInstruction.js +11 -1
- package/dist/server/Vm.js +42 -27
- package/package.json +3 -2
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#include "environment.h"
|
|
2
|
+
#include "executor.h"
|
|
3
|
+
#include "node_wrapper.h"
|
|
4
|
+
#include "scheduler.h"
|
|
5
|
+
#include <memory>
|
|
6
|
+
#include <v8.h>
|
|
7
|
+
#include <utility>
|
|
8
|
+
|
|
9
|
+
using namespace v8;
|
|
10
|
+
namespace ivm {
|
|
11
|
+
namespace {
|
|
12
|
+
thread_pool_t thread_pool{std::thread::hardware_concurrency() + 1};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* Scheduler implementation
|
|
17
|
+
*/
|
|
18
|
+
void Scheduler::CancelAsync() {
|
|
19
|
+
if (async_wait != nullptr) {
|
|
20
|
+
async_wait->Cancel();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
void Scheduler::DoneRunning() {
|
|
25
|
+
assert(status == Status::Running);
|
|
26
|
+
status = Status::Waiting;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
void Scheduler::InterruptIsolate() {
|
|
30
|
+
assert(status == Status::Running);
|
|
31
|
+
// Since this callback will be called by v8 we can be certain the pointer to `isolate` is still valid
|
|
32
|
+
env.GetIsolate()->RequestInterrupt([](Isolate* /*isolate_ptr*/, void* env_ptr) {
|
|
33
|
+
static_cast<IsolateEnvironment*>(env_ptr)->InterruptEntryAsync();
|
|
34
|
+
}, &env);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
void Scheduler::InterruptSyncIsolate() {
|
|
38
|
+
env.GetIsolate()->RequestInterrupt([](Isolate* /*isolate_ptr*/, void* env_ptr) {
|
|
39
|
+
static_cast<IsolateEnvironment*>(env_ptr)->InterruptEntrySync();
|
|
40
|
+
}, &env);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
auto Scheduler::WakeIsolate(std::shared_ptr<IsolateEnvironment> isolate_ptr) -> bool {
|
|
44
|
+
if (status == Status::Waiting) {
|
|
45
|
+
status = Status::Running;
|
|
46
|
+
// Move shared reference to this scheduler to ensure the IsolateEnvironment won't be deleted
|
|
47
|
+
// before a thread picks up this work.
|
|
48
|
+
assert(!env_ref);
|
|
49
|
+
env_ref = std::move(isolate_ptr);
|
|
50
|
+
IncrementUvRef();
|
|
51
|
+
SendWake();
|
|
52
|
+
return true;
|
|
53
|
+
} else {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
auto LockedScheduler::GetForegroundTaskRunner() -> std::shared_ptr<v8::TaskRunner> {
|
|
59
|
+
return env.GetTaskRunner();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
void LockedScheduler::DecrementUvRefForIsolate(const std::shared_ptr<IsolateHolder>& holder) {
|
|
63
|
+
auto ref = holder->GetIsolate();
|
|
64
|
+
if (ref) {
|
|
65
|
+
ref->scheduler->DecrementUvRef();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
void LockedScheduler::IncrementUvRefForIsolate(const std::shared_ptr<IsolateHolder>& holder) {
|
|
70
|
+
auto ref = holder->GetIsolate();
|
|
71
|
+
if (ref) {
|
|
72
|
+
ref->scheduler->IncrementUvRef();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
IsolatedScheduler::IsolatedScheduler(IsolateEnvironment& env, UvScheduler& default_scheduler) :
|
|
77
|
+
LockedScheduler{env},
|
|
78
|
+
default_scheduler{default_scheduler} {}
|
|
79
|
+
|
|
80
|
+
void IsolatedScheduler::DecrementUvRef() {
|
|
81
|
+
default_scheduler.DecrementUvRef();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
void IsolatedScheduler::IncrementUvRef() {
|
|
85
|
+
default_scheduler.IncrementUvRef();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
void IsolatedScheduler::SendWake() {
|
|
89
|
+
thread_pool.exec(thread_affinity, [](bool pool_thread, void* param) {
|
|
90
|
+
auto& scheduler = *static_cast<IsolatedScheduler*>(param);
|
|
91
|
+
auto ref = std::exchange(scheduler.env_ref, {});
|
|
92
|
+
ref->AsyncEntry();
|
|
93
|
+
if (!pool_thread) {
|
|
94
|
+
ref->GetIsolate()->DiscardThreadSpecificMetadata();
|
|
95
|
+
}
|
|
96
|
+
// Grab reference to default scheduler, since resetting `ref` may deallocate `scheduler` and
|
|
97
|
+
// invalidate the instance. Resetting `ref` must take place here because the destructor might
|
|
98
|
+
// invoke cleanup tasks on the default isolate which will increment `uv_ref_count`.
|
|
99
|
+
// `uv_ref_count` needs to be incremented before it's decremented otherwise `UvScheduler` will
|
|
100
|
+
// try to invoke `uv_ref` from a non-default thread.
|
|
101
|
+
auto& default_scheduler = scheduler.default_scheduler;
|
|
102
|
+
ref = {};
|
|
103
|
+
if (--default_scheduler.uv_ref_count == 0) {
|
|
104
|
+
// Wake up the libuv loop so we can unref the async handle from the default thread.
|
|
105
|
+
uv_async_send(default_scheduler.uv_async);
|
|
106
|
+
}
|
|
107
|
+
}, this);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
UvScheduler::UvScheduler(IsolateEnvironment& env) :
|
|
111
|
+
LockedScheduler{env},
|
|
112
|
+
loop{node::GetCurrentEventLoop(v8::Isolate::GetCurrent())},
|
|
113
|
+
uv_async{new uv_async_t} {
|
|
114
|
+
uv_async_init(loop, uv_async, [](uv_async_t* async) {
|
|
115
|
+
auto& scheduler = *static_cast<UvScheduler*>(async->data);
|
|
116
|
+
auto ref = [&]() {
|
|
117
|
+
// Lock is required to access env_ref on the default scheduler but a non-default scheduler
|
|
118
|
+
// doesn't need it. This is because `WakeIsolate` can trigger this function via
|
|
119
|
+
// `uv_async_send` while another thread is writing to `env_ref`
|
|
120
|
+
std::lock_guard<std::mutex> lock{scheduler.mutex};
|
|
121
|
+
return std::exchange(scheduler.env_ref, {});
|
|
122
|
+
}();
|
|
123
|
+
if (ref) {
|
|
124
|
+
ref->AsyncEntry();
|
|
125
|
+
if (--scheduler.uv_ref_count == 0) {
|
|
126
|
+
uv_unref(reinterpret_cast<uv_handle_t*>(scheduler.uv_async));
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
if (scheduler.uv_ref_count.load() == 0) {
|
|
130
|
+
uv_unref(reinterpret_cast<uv_handle_t*>(scheduler.uv_async));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
uv_async->data = this;
|
|
135
|
+
uv_unref(reinterpret_cast<uv_handle_t*>(uv_async));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
UvScheduler::~UvScheduler() {
|
|
139
|
+
uv_close(reinterpret_cast<uv_handle_t*>(uv_async), [](uv_handle_t* handle) {
|
|
140
|
+
delete reinterpret_cast<uv_async_t*>(handle);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
void UvScheduler::DecrementUvRef() {
|
|
145
|
+
if (--uv_ref_count == 0) {
|
|
146
|
+
if (Executor::IsDefaultThread()) {
|
|
147
|
+
uv_unref(reinterpret_cast<uv_handle_t*>(uv_async));
|
|
148
|
+
} else {
|
|
149
|
+
uv_async_send(uv_async);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
void UvScheduler::IncrementUvRef() {
|
|
155
|
+
if (++uv_ref_count == 1) {
|
|
156
|
+
// Only the default thread should be able to reach this branch
|
|
157
|
+
assert(Executor::IsDefaultThread());
|
|
158
|
+
uv_ref(reinterpret_cast<uv_handle_t*>(uv_async));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
void UvScheduler::SendWake() {
|
|
163
|
+
uv_async_send(uv_async);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/*
|
|
167
|
+
* Scheduler::AsyncWait implementation
|
|
168
|
+
*/
|
|
169
|
+
Scheduler::AsyncWait::~AsyncWait() {
|
|
170
|
+
scheduler.Lock()->async_wait = nullptr;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
void Scheduler::AsyncWait::Cancel() {
|
|
174
|
+
*state.write() = canceled;
|
|
175
|
+
state.notify_one();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
void Scheduler::AsyncWait::Done() {
|
|
179
|
+
*state.write() = finished;
|
|
180
|
+
state.notify_one();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
auto Scheduler::AsyncWait::Wait() const -> Scheduler::AsyncWait::State {
|
|
184
|
+
auto lock = state.read<true>();
|
|
185
|
+
while (*lock == pending) {
|
|
186
|
+
lock.wait();
|
|
187
|
+
}
|
|
188
|
+
return *lock;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
} // namespace ivm
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include "platform_delegate.h"
|
|
3
|
+
#include "runnable.h"
|
|
4
|
+
#include "lib/lockable.h"
|
|
5
|
+
#include "lib/thread_pool.h"
|
|
6
|
+
#include <uv.h>
|
|
7
|
+
#include <atomic>
|
|
8
|
+
#include <condition_variable>
|
|
9
|
+
#include <memory>
|
|
10
|
+
#include <mutex>
|
|
11
|
+
#include <queue>
|
|
12
|
+
|
|
13
|
+
namespace ivm {
|
|
14
|
+
class IsolateEnvironment;
|
|
15
|
+
class IsolateHolder;
|
|
16
|
+
|
|
17
|
+
// gcc 5's std::queue implementation has an explicit default ctor so std::exchange(..., {})
|
|
18
|
+
// doesn't work. This changed in C++11 and I guess they're behind.
|
|
19
|
+
template <class Type>
|
|
20
|
+
auto ExchangeDefault(Type& container) {
|
|
21
|
+
return std::exchange(container, Type{});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Keeps track of tasks an isolate needs to run and manages its run state (running or waiting).
|
|
26
|
+
* This does all the interaction with libuv async and the thread pool.
|
|
27
|
+
*/
|
|
28
|
+
class UvScheduler;
|
|
29
|
+
class Scheduler {
|
|
30
|
+
friend IsolateEnvironment;
|
|
31
|
+
friend class LockedScheduler;
|
|
32
|
+
private:
|
|
33
|
+
using TaskQueue = std::queue<std::unique_ptr<Runnable>>;
|
|
34
|
+
|
|
35
|
+
public:
|
|
36
|
+
explicit Scheduler(IsolateEnvironment& env) : env{env} {}
|
|
37
|
+
Scheduler(const Scheduler&) = delete;
|
|
38
|
+
~Scheduler() = default;
|
|
39
|
+
auto operator=(const Scheduler&) = delete;
|
|
40
|
+
|
|
41
|
+
// Request cancellation of current async task
|
|
42
|
+
void CancelAsync();
|
|
43
|
+
// Called after AsyncEntry finishes
|
|
44
|
+
void DoneRunning();
|
|
45
|
+
// Request an interrupt in this isolate. `status` must == Running to invoke this.
|
|
46
|
+
void InterruptIsolate();
|
|
47
|
+
// Interrupts an isolate running in the default thread
|
|
48
|
+
void InterruptSyncIsolate();
|
|
49
|
+
// Returns true if a wake was scheduled, false if the isolate is already running.
|
|
50
|
+
auto WakeIsolate(std::shared_ptr<IsolateEnvironment> isolate_ptr) -> bool;
|
|
51
|
+
|
|
52
|
+
// Scheduler::AsyncWait will pause the current thread until woken up by another thread
|
|
53
|
+
class AsyncWait {
|
|
54
|
+
public:
|
|
55
|
+
enum State { pending, finished, canceled };
|
|
56
|
+
|
|
57
|
+
// This is templated so we can cast to the protected base class here instead of at the call
|
|
58
|
+
// site
|
|
59
|
+
template <class Type>
|
|
60
|
+
explicit AsyncWait(Type& scheduler) :
|
|
61
|
+
scheduler{scheduler} {
|
|
62
|
+
auto lock = scheduler.Lock();
|
|
63
|
+
assert(lock->async_wait == nullptr);
|
|
64
|
+
lock->async_wait = this;
|
|
65
|
+
}
|
|
66
|
+
AsyncWait(const AsyncWait&) = delete;
|
|
67
|
+
~AsyncWait();
|
|
68
|
+
auto operator=(const AsyncWait&) = delete;
|
|
69
|
+
|
|
70
|
+
void Cancel();
|
|
71
|
+
void Done();
|
|
72
|
+
auto Wait() const -> State;
|
|
73
|
+
|
|
74
|
+
class LockedScheduler& scheduler;
|
|
75
|
+
lockable_t<State, false, true> state{pending};
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Task queues
|
|
79
|
+
TaskQueue tasks;
|
|
80
|
+
TaskQueue handle_tasks;
|
|
81
|
+
TaskQueue interrupts;
|
|
82
|
+
TaskQueue sync_interrupts;
|
|
83
|
+
|
|
84
|
+
protected:
|
|
85
|
+
mutable std::mutex mutex;
|
|
86
|
+
mutable std::condition_variable cv;
|
|
87
|
+
std::shared_ptr<IsolateEnvironment> env_ref;
|
|
88
|
+
IsolateEnvironment& env;
|
|
89
|
+
|
|
90
|
+
private:
|
|
91
|
+
virtual void IncrementUvRef() = 0;
|
|
92
|
+
virtual void DecrementUvRef() = 0;
|
|
93
|
+
virtual void SendWake() = 0;
|
|
94
|
+
|
|
95
|
+
enum class Status { Waiting, Running };
|
|
96
|
+
AsyncWait* async_wait = nullptr;
|
|
97
|
+
Status status = Status::Waiting;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
class LockedScheduler : protected Scheduler, public node::IsolatePlatformDelegate {
|
|
101
|
+
friend Scheduler::AsyncWait;
|
|
102
|
+
public:
|
|
103
|
+
using Scheduler::Scheduler;
|
|
104
|
+
|
|
105
|
+
// Locks the scheduler and return a lock with access to public interface
|
|
106
|
+
auto Lock() {
|
|
107
|
+
// TODO: Would be nice to find a way to roll this into the lockable abstraction
|
|
108
|
+
class Lock {
|
|
109
|
+
public:
|
|
110
|
+
auto operator*() -> auto& { return scheduler; }
|
|
111
|
+
auto operator->() { return &scheduler; }
|
|
112
|
+
|
|
113
|
+
explicit Lock(Scheduler& scheduler, std::mutex& mutex) :
|
|
114
|
+
lock{mutex},
|
|
115
|
+
scheduler{scheduler} {}
|
|
116
|
+
|
|
117
|
+
private:
|
|
118
|
+
std::unique_lock<std::mutex> lock;
|
|
119
|
+
Scheduler& scheduler;
|
|
120
|
+
};
|
|
121
|
+
return Lock{*this, mutex};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// IsolatePlatformDelegate overrides
|
|
125
|
+
auto GetForegroundTaskRunner() -> std::shared_ptr<v8::TaskRunner> final;
|
|
126
|
+
auto IdleTasksEnabled() -> bool final { return false; }
|
|
127
|
+
|
|
128
|
+
// Used to ref/unref the uv handle from C++ API
|
|
129
|
+
static void DecrementUvRefForIsolate(const std::shared_ptr<IsolateHolder>& holder);
|
|
130
|
+
static void IncrementUvRefForIsolate(const std::shared_ptr<IsolateHolder>& holder);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
class IsolatedScheduler final : public LockedScheduler {
|
|
134
|
+
public:
|
|
135
|
+
explicit IsolatedScheduler(IsolateEnvironment& env, UvScheduler& default_scheduler);
|
|
136
|
+
|
|
137
|
+
private:
|
|
138
|
+
void DecrementUvRef() override;
|
|
139
|
+
void IncrementUvRef() override;
|
|
140
|
+
void SendWake() override;
|
|
141
|
+
|
|
142
|
+
thread_pool_t::affinity_t thread_affinity;
|
|
143
|
+
UvScheduler& default_scheduler;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
class UvScheduler final : public LockedScheduler {
|
|
147
|
+
friend IsolatedScheduler;
|
|
148
|
+
friend Scheduler;
|
|
149
|
+
public:
|
|
150
|
+
explicit UvScheduler(IsolateEnvironment& env);
|
|
151
|
+
UvScheduler(const UvScheduler&) = delete;
|
|
152
|
+
~UvScheduler();
|
|
153
|
+
auto operator=(const UvScheduler&) = delete;
|
|
154
|
+
|
|
155
|
+
private:
|
|
156
|
+
void DecrementUvRef() override;
|
|
157
|
+
void IncrementUvRef() override;
|
|
158
|
+
void SendWake() override;
|
|
159
|
+
|
|
160
|
+
uv_loop_t* loop = nullptr;
|
|
161
|
+
uv_async_t* uv_async = nullptr;
|
|
162
|
+
std::atomic<int> uv_ref_count{0};
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
} // namespace ivm
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include <v8.h>
|
|
3
|
+
#include <atomic>
|
|
4
|
+
|
|
5
|
+
namespace ivm {
|
|
6
|
+
namespace detail {
|
|
7
|
+
extern std::atomic<size_t> IsolateSpecificSize;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Like thread_local data, but specific to an Isolate instead.
|
|
12
|
+
*/
|
|
13
|
+
template <class Type>
|
|
14
|
+
class IsolateSpecific {
|
|
15
|
+
template <class>
|
|
16
|
+
friend class IsolateSpecific;
|
|
17
|
+
|
|
18
|
+
public:
|
|
19
|
+
IsolateSpecific() = default;
|
|
20
|
+
|
|
21
|
+
template <class Functor>
|
|
22
|
+
auto Deref(Functor callback) -> v8::Local<Type>;
|
|
23
|
+
|
|
24
|
+
private:
|
|
25
|
+
size_t key{detail::IsolateSpecificSize++};
|
|
26
|
+
union HandleConvert {
|
|
27
|
+
explicit HandleConvert(v8::Local<v8::Data> data) : data{data} {}
|
|
28
|
+
v8::Local<v8::Data> data;
|
|
29
|
+
v8::Local<Type> value;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
} // namespace ivm
|
|
34
|
+
|
|
35
|
+
#include "environment.h"
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
#include "stack_trace.h"
|
|
2
|
+
#include "functor_runners.h"
|
|
3
|
+
#include "specific.h"
|
|
4
|
+
#include "v8_version.h"
|
|
5
|
+
#include <cstring>
|
|
6
|
+
#include <sstream>
|
|
7
|
+
#include <string>
|
|
8
|
+
|
|
9
|
+
using namespace v8;
|
|
10
|
+
|
|
11
|
+
namespace ivm {
|
|
12
|
+
|
|
13
|
+
static auto StringConcat(Isolate* isolate, Local<String> left, Local<String> right) -> Local<String> {
|
|
14
|
+
return String::Concat(isolate, left, right);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* This returns an object that's like Symbol() in JS but only C++ can see it.
|
|
19
|
+
*/
|
|
20
|
+
auto GetPrivateStackSymbol() -> Local<Private> {
|
|
21
|
+
static IsolateSpecific<Private> holder;
|
|
22
|
+
return holder.Deref([]() {
|
|
23
|
+
return Private::New(Isolate::GetCurrent());
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Helper which renders to string either a String (pass-through), StackTrace (render), or Array
|
|
29
|
+
* (recursion pair)
|
|
30
|
+
*/
|
|
31
|
+
auto RenderErrorStack(Local<Value> data) -> Local<String> {
|
|
32
|
+
Isolate* isolate = Isolate::GetCurrent();
|
|
33
|
+
if (data->IsString()) {
|
|
34
|
+
// Plain string. We need to remove the first line of `stack` to avoid repeating the error
|
|
35
|
+
// message
|
|
36
|
+
String::Utf8Value string_value{isolate, data.As<String>()};
|
|
37
|
+
const char* c_str = *string_value;
|
|
38
|
+
// If it starts with " at" then the message has already been removed
|
|
39
|
+
if (strncmp(c_str, " at", 6) == 0) {
|
|
40
|
+
return data.As<String>();
|
|
41
|
+
}
|
|
42
|
+
// Find the newline
|
|
43
|
+
const char* pos = strchr(c_str, '\n');
|
|
44
|
+
while (pos != nullptr) {
|
|
45
|
+
if (strncmp(pos + 1, " at", 6) == 0) {
|
|
46
|
+
// Found the start of the stack
|
|
47
|
+
return Unmaybe(String::NewFromOneByte(isolate, reinterpret_cast<const uint8_t*>(pos), NewStringType::kNormal));
|
|
48
|
+
}
|
|
49
|
+
pos = strchr(pos + 1, '\n');
|
|
50
|
+
}
|
|
51
|
+
// No stack, just a message
|
|
52
|
+
return String::Empty(isolate);
|
|
53
|
+
|
|
54
|
+
} else if (data->IsArray()) {
|
|
55
|
+
// Array pair
|
|
56
|
+
Local<Context> context = isolate->GetCurrentContext();
|
|
57
|
+
Local<Array> array = data.As<Array>();
|
|
58
|
+
return StringConcat(isolate,
|
|
59
|
+
StringConcat(isolate,
|
|
60
|
+
RenderErrorStack(Unmaybe(array->Get(context, 1))),
|
|
61
|
+
Unmaybe(String::NewFromOneByte(isolate, reinterpret_cast<const uint8_t*>("\n at (<isolated-vm boundary>)"), NewStringType::kNormal))
|
|
62
|
+
),
|
|
63
|
+
RenderErrorStack(Unmaybe(array->Get(context, 0)))
|
|
64
|
+
);
|
|
65
|
+
} else {
|
|
66
|
+
// StackTraceHolder
|
|
67
|
+
StackTraceHolder& that = *ClassHandle::Unwrap<StackTraceHolder>(data.As<Object>());
|
|
68
|
+
Local<StackTrace> stack_trace = Deref(that.stack_trace);
|
|
69
|
+
return Unmaybe(String::NewFromUtf8(isolate, StackTraceHolder::RenderSingleStack(stack_trace).c_str(), NewStringType::kNormal));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Accessor on error `stack`. Renders from previously saved stack trace.
|
|
75
|
+
*/
|
|
76
|
+
void ErrorStackGetter(Local<Name> /*property*/, const PropertyCallbackInfo<Value>& info) {
|
|
77
|
+
FunctorRunners::RunCallback(info, [ &info ]() {
|
|
78
|
+
Isolate* isolate = Isolate::GetCurrent();
|
|
79
|
+
Local<Context> context = isolate->GetCurrentContext();
|
|
80
|
+
Local<Object> holder = info.This();
|
|
81
|
+
Local<Value> name = Unmaybe(holder->Get(context, StringTable::Get().name));
|
|
82
|
+
if (!name->IsString()) {
|
|
83
|
+
name = holder->GetConstructorName();
|
|
84
|
+
}
|
|
85
|
+
return StringConcat(isolate,
|
|
86
|
+
StringConcat(isolate, name.As<String>(), StringTable::Get().colonSpace),
|
|
87
|
+
StringConcat(isolate,
|
|
88
|
+
Unmaybe(
|
|
89
|
+
Unmaybe(info.This()->Get(context, StringTable::Get().message))->ToString(context)
|
|
90
|
+
),
|
|
91
|
+
RenderErrorStack(Unmaybe(holder->GetPrivate(context, GetPrivateStackSymbol())))
|
|
92
|
+
)
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Utility function which sets the stack getter on an error object
|
|
99
|
+
*/
|
|
100
|
+
void AttachStackGetter(Local<Object> error, Local<Value> data) {
|
|
101
|
+
Local<Context> context = Isolate::GetCurrent()->GetCurrentContext();
|
|
102
|
+
Unmaybe(error->SetPrivate(context, GetPrivateStackSymbol(), data));
|
|
103
|
+
Unmaybe(error->SetAccessor(
|
|
104
|
+
context,
|
|
105
|
+
StringTable::Get().stack,
|
|
106
|
+
ErrorStackGetter, nullptr,
|
|
107
|
+
Local<Value>(),
|
|
108
|
+
AccessControl::DEFAULT,
|
|
109
|
+
PropertyAttribute::DontEnum
|
|
110
|
+
));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* StackTraceHolder implementation
|
|
115
|
+
*/
|
|
116
|
+
StackTraceHolder::StackTraceHolder(Local<StackTrace> stack_handle) : stack_trace(Isolate::GetCurrent(), stack_handle) {}
|
|
117
|
+
|
|
118
|
+
auto StackTraceHolder::Definition() -> Local<FunctionTemplate> {
|
|
119
|
+
return MakeClass("StackTraceHolder", nullptr);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
void StackTraceHolder::AttachStack(Local<Object> error, Local<StackTrace> stack) {
|
|
123
|
+
AttachStackGetter(error, ClassHandle::NewInstance<StackTraceHolder>(stack));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
void StackTraceHolder::ChainStack(Local<Object> error, Local<StackTrace> stack) {
|
|
127
|
+
Isolate* isolate = Isolate::GetCurrent();
|
|
128
|
+
Local<Context> context = isolate->GetCurrentContext();
|
|
129
|
+
Local<Value> existing_data = Unmaybe(error->GetPrivate(context, GetPrivateStackSymbol()));
|
|
130
|
+
if (existing_data->IsUndefined()) {
|
|
131
|
+
// This error has not passed through ivm yet. Get the existing stack trace.
|
|
132
|
+
Local<StackTrace> existing_stack = Exception::GetStackTrace(error);
|
|
133
|
+
if (existing_stack.IsEmpty()) {
|
|
134
|
+
// In this case it's probably passed through ExternalCopy which flattens the `stack` property
|
|
135
|
+
// into a plain value
|
|
136
|
+
existing_data = Unmaybe(error->Get(context, StringTable::Get().stack));
|
|
137
|
+
if (existing_data->IsUndefined() || !existing_data->IsString()) {
|
|
138
|
+
return AttachStack(error, stack);
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
existing_data = ClassHandle::NewInstance<StackTraceHolder>(existing_stack);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
Local<Array> pair = Array::New(isolate, 2);
|
|
145
|
+
Unmaybe(pair->Set(context, 0, ClassHandle::NewInstance<StackTraceHolder>(stack)));
|
|
146
|
+
Unmaybe(pair->Set(context, 1, existing_data));
|
|
147
|
+
AttachStackGetter(error, pair);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
auto StackTraceHolder::RenderSingleStack(Local<StackTrace> stack_trace) -> std::string {
|
|
151
|
+
Isolate* isolate = Isolate::GetCurrent();
|
|
152
|
+
std::stringstream ss;
|
|
153
|
+
int size = stack_trace->GetFrameCount();
|
|
154
|
+
for (int ii = 0; ii < size; ++ii) {
|
|
155
|
+
Local<StackFrame> frame = stack_trace->GetFrame(isolate, ii);
|
|
156
|
+
ss <<"\n at ";
|
|
157
|
+
String::Utf8Value fn_name{isolate, frame->GetFunctionName()};
|
|
158
|
+
if (frame->IsWasm()) {
|
|
159
|
+
String::Utf8Value script_name{isolate, frame->GetScriptName()};
|
|
160
|
+
bool has_name = fn_name.length() != 0 || script_name.length() != 0;
|
|
161
|
+
if (has_name) {
|
|
162
|
+
if (script_name.length() == 0) {
|
|
163
|
+
ss <<*fn_name;
|
|
164
|
+
} else {
|
|
165
|
+
ss <<*script_name;
|
|
166
|
+
if (fn_name.length() != 0) {
|
|
167
|
+
ss <<"." <<*fn_name;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
ss <<" (<WASM>";
|
|
171
|
+
}
|
|
172
|
+
ss <<frame->GetLineNumber() <<":" <<frame->GetColumn();
|
|
173
|
+
if (has_name) {
|
|
174
|
+
ss <<")";
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
if (frame->IsConstructor()) {
|
|
178
|
+
ss <<"new ";
|
|
179
|
+
if (fn_name.length() == 0) {
|
|
180
|
+
ss <<"<anonymous>";
|
|
181
|
+
} else {
|
|
182
|
+
ss <<*fn_name;
|
|
183
|
+
}
|
|
184
|
+
} else if (fn_name.length() != 0) {
|
|
185
|
+
ss <<*fn_name;
|
|
186
|
+
} else {
|
|
187
|
+
AppendFileLocation(isolate, frame, ss);
|
|
188
|
+
return ss.str();
|
|
189
|
+
}
|
|
190
|
+
ss <<" (";
|
|
191
|
+
AppendFileLocation(isolate, frame, ss);
|
|
192
|
+
ss <<")";
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return ss.str();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
void StackTraceHolder::AppendFileLocation(Isolate* /*isolate*/, Local<StackFrame> frame, std::stringstream& ss) {
|
|
199
|
+
String::Utf8Value script_name{Isolate::GetCurrent(), frame->GetScriptNameOrSourceURL()};
|
|
200
|
+
if (script_name.length() == 0) {
|
|
201
|
+
if (frame->IsEval()) {
|
|
202
|
+
ss <<"[eval]";
|
|
203
|
+
} else {
|
|
204
|
+
ss <<"<anonymous>";
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
ss <<*script_name;
|
|
208
|
+
}
|
|
209
|
+
int line_number = frame->GetLineNumber();
|
|
210
|
+
if (line_number != -1) {
|
|
211
|
+
ss <<":" <<line_number;
|
|
212
|
+
int column = frame->GetColumn();
|
|
213
|
+
if (column != -1) {
|
|
214
|
+
ss <<":" <<column;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
} // namespace ivm
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include <v8.h>
|
|
3
|
+
#include "class_handle.h"
|
|
4
|
+
#include <string>
|
|
5
|
+
|
|
6
|
+
namespace ivm {
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* v8::StackTrace doesn't inherit from v8::Value so it can't be set as a property of an object which
|
|
10
|
+
* is what we need to do to pass stack traces in an efficent way. So this gets around that.
|
|
11
|
+
*/
|
|
12
|
+
class StackTraceHolder : public ClassHandle {
|
|
13
|
+
private:
|
|
14
|
+
static void AppendFileLocation(v8::Isolate* isolate, v8::Local<v8::StackFrame> frame, std::stringstream& ss);
|
|
15
|
+
public:
|
|
16
|
+
v8::Persistent<v8::StackTrace, v8::CopyablePersistentTraits<v8::StackTrace>> stack_trace;
|
|
17
|
+
explicit StackTraceHolder(v8::Local<v8::StackTrace> stack_handle);
|
|
18
|
+
static auto Definition() -> v8::Local<v8::FunctionTemplate>;
|
|
19
|
+
static void AttachStack(v8::Local<v8::Object> error, v8::Local<v8::StackTrace> stack);
|
|
20
|
+
static void ChainStack(v8::Local<v8::Object> error, v8::Local<v8::StackTrace> stack);
|
|
21
|
+
static auto RenderSingleStack(v8::Local<v8::StackTrace> stack_trace) -> std::string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
} // namespace ivm
|