@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,626 @@
|
|
|
1
|
+
#include "environment.h"
|
|
2
|
+
#include "allocator.h"
|
|
3
|
+
#include "inspector.h"
|
|
4
|
+
#include "isolate/cpu_profile_manager.h"
|
|
5
|
+
#include "isolate/generic/error.h"
|
|
6
|
+
#include "platform_delegate.h"
|
|
7
|
+
#include "runnable.h"
|
|
8
|
+
#include "external_copy/external_copy.h"
|
|
9
|
+
#include "scheduler.h"
|
|
10
|
+
#include "lib/suspend.h"
|
|
11
|
+
#include <algorithm>
|
|
12
|
+
#include <chrono>
|
|
13
|
+
#include <climits>
|
|
14
|
+
#include <cmath>
|
|
15
|
+
#include <cstdio>
|
|
16
|
+
#include <memory>
|
|
17
|
+
#include <mutex>
|
|
18
|
+
#include <thread>
|
|
19
|
+
#include <vector>
|
|
20
|
+
#include "v8-platform.h"
|
|
21
|
+
#include "v8.h"
|
|
22
|
+
|
|
23
|
+
#if USE_CLOCK_THREAD_CPUTIME_ID
|
|
24
|
+
#include <time.h>
|
|
25
|
+
#endif
|
|
26
|
+
|
|
27
|
+
#if defined __APPLE__
|
|
28
|
+
#include <pthread.h>
|
|
29
|
+
static auto GetStackBase() -> void* {
|
|
30
|
+
pthread_t self = pthread_self();
|
|
31
|
+
return (void*)((char*)pthread_get_stackaddr_np(self) - pthread_get_stacksize_np(self));
|
|
32
|
+
}
|
|
33
|
+
#elif defined __OpenBSD__
|
|
34
|
+
#include <pthread_np.h>
|
|
35
|
+
static auto GetStackBase() -> void* {
|
|
36
|
+
pthread_t self = pthread_self();
|
|
37
|
+
stack_t ss;
|
|
38
|
+
void* base = nullptr;
|
|
39
|
+
if (pthread_stackseg_np(self, &ss) == 0) {
|
|
40
|
+
base = (void*)((char*)ss.ss_sp - ss.ss_size);
|
|
41
|
+
}
|
|
42
|
+
return base;
|
|
43
|
+
#elif defined __FreeBSD__
|
|
44
|
+
#include <pthread_np.h>
|
|
45
|
+
static auto GetStackBase() -> void* {
|
|
46
|
+
pthread_t self = pthread_self();
|
|
47
|
+
pthread_attr_t attrs;
|
|
48
|
+
void* base = nullptr;
|
|
49
|
+
if (pthread_attr_init(&attrs) == 0) {
|
|
50
|
+
pthread_attr_get_np(self, &attrs);
|
|
51
|
+
size_t size;
|
|
52
|
+
pthread_attr_getstack(&attrs, &base, &size);
|
|
53
|
+
pthread_attr_destroy(&attrs);
|
|
54
|
+
}
|
|
55
|
+
return base;
|
|
56
|
+
}
|
|
57
|
+
#elif defined __unix__
|
|
58
|
+
static auto GetStackBase() -> void* {
|
|
59
|
+
pthread_t self = pthread_self();
|
|
60
|
+
pthread_attr_t attrs;
|
|
61
|
+
pthread_getattr_np(self, &attrs);
|
|
62
|
+
void* base;
|
|
63
|
+
size_t size;
|
|
64
|
+
pthread_attr_getstack(&attrs, &base, &size);
|
|
65
|
+
return base;
|
|
66
|
+
}
|
|
67
|
+
#else
|
|
68
|
+
static void* GetStackBase() {
|
|
69
|
+
return nullptr;
|
|
70
|
+
}
|
|
71
|
+
#endif
|
|
72
|
+
|
|
73
|
+
using namespace v8;
|
|
74
|
+
using std::shared_ptr;
|
|
75
|
+
using std::unique_ptr;
|
|
76
|
+
|
|
77
|
+
namespace ivm {
|
|
78
|
+
|
|
79
|
+
std::atomic<size_t> detail::IsolateSpecificSize{0};
|
|
80
|
+
thread_suspend_handle::initialize suspend_init{};
|
|
81
|
+
|
|
82
|
+
namespace {
|
|
83
|
+
std::mutex isolate_allocator_mutex{};
|
|
84
|
+
} // anonymous namespace
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* HeapCheck implementation
|
|
88
|
+
*/
|
|
89
|
+
IsolateEnvironment::HeapCheck::HeapCheck(IsolateEnvironment& env, bool force) :
|
|
90
|
+
env{env}, extra_size_before{env.extra_allocated_memory}, force{force} {
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
void IsolateEnvironment::HeapCheck::Epilogue() {
|
|
94
|
+
if (!env.nodejs_isolate && (force || env.extra_allocated_memory != extra_size_before)) {
|
|
95
|
+
Isolate* isolate = env.GetIsolate();
|
|
96
|
+
HeapStatistics heap;
|
|
97
|
+
isolate->GetHeapStatistics(&heap);
|
|
98
|
+
if (heap.used_heap_size() + env.extra_allocated_memory > env.memory_limit) {
|
|
99
|
+
isolate->LowMemoryNotification();
|
|
100
|
+
isolate->GetHeapStatistics(&heap);
|
|
101
|
+
if (heap.used_heap_size() + env.extra_allocated_memory > env.memory_limit) {
|
|
102
|
+
env.hit_memory_limit = true;
|
|
103
|
+
env.Terminate();
|
|
104
|
+
throw FatalRuntimeError("Isolate was disposed during execution due to memory limit");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* IsolateEnvironment implementation
|
|
112
|
+
*/
|
|
113
|
+
void IsolateEnvironment::OOMErrorCallback(const char* location, bool is_heap_oom) {
|
|
114
|
+
if (RaiseCatastrophicError(IsolateEnvironment::GetCurrent().error_handler, "Catastrophic out-of-memory error")) {
|
|
115
|
+
while (true) {
|
|
116
|
+
using namespace std::chrono_literals;
|
|
117
|
+
std::this_thread::sleep_for(100s);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
fprintf(stderr, "%s\nis_heap_oom = %d\n\n\n", location, static_cast<int>(is_heap_oom));
|
|
121
|
+
HeapStatistics heap;
|
|
122
|
+
Isolate::GetCurrent()->GetHeapStatistics(&heap);
|
|
123
|
+
fprintf(stderr,
|
|
124
|
+
"<--- Heap statistics --->\n"
|
|
125
|
+
"total_heap_size = %zd\n"
|
|
126
|
+
"total_heap_size_executable = %zd\n"
|
|
127
|
+
"total_physical_size = %zd\n"
|
|
128
|
+
"total_available_size = %zd\n"
|
|
129
|
+
"used_heap_size = %zd\n"
|
|
130
|
+
"heap_size_limit = %zd\n"
|
|
131
|
+
"malloced_memory = %zd\n"
|
|
132
|
+
"peak_malloced_memory = %zd\n"
|
|
133
|
+
"does_zap_garbage = %zd\n",
|
|
134
|
+
heap.total_heap_size(),
|
|
135
|
+
heap.total_heap_size_executable(),
|
|
136
|
+
heap.total_physical_size(),
|
|
137
|
+
heap.total_available_size(),
|
|
138
|
+
heap.used_heap_size(),
|
|
139
|
+
heap.heap_size_limit(),
|
|
140
|
+
heap.malloced_memory(),
|
|
141
|
+
heap.peak_malloced_memory(),
|
|
142
|
+
heap.does_zap_garbage()
|
|
143
|
+
);
|
|
144
|
+
abort();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
void IsolateEnvironment::PromiseRejectCallback(PromiseRejectMessage rejection) {
|
|
148
|
+
auto& that = IsolateEnvironment::GetCurrent();
|
|
149
|
+
assert(that.isolate == Isolate::GetCurrent());
|
|
150
|
+
// TODO: Revisit this in version 4.x?
|
|
151
|
+
auto event = rejection.GetEvent();
|
|
152
|
+
if (event == kPromiseRejectWithNoHandler) {
|
|
153
|
+
that.unhandled_promise_rejections.emplace_back(that.isolate, rejection.GetPromise());
|
|
154
|
+
that.unhandled_promise_rejections.back().SetWeak();
|
|
155
|
+
} else if (event == kPromiseHandlerAddedAfterReject) {
|
|
156
|
+
that.PromiseWasHandled(rejection.GetPromise());
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
void IsolateEnvironment::PromiseWasHandled(v8::Local<v8::Promise> promise) {
|
|
161
|
+
for (auto& handle : unhandled_promise_rejections) {
|
|
162
|
+
if (handle == promise) {
|
|
163
|
+
handle.Reset();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
auto IsolateEnvironment::CodeGenCallback(Local<Context> /*context*/, Local<Value> source) -> ModifyCodeGenerationFromStringsResult {
|
|
169
|
+
auto& that = IsolateEnvironment::GetCurrent();
|
|
170
|
+
// This heuristic could be improved by looking up how much `timeout` this isolate has left and
|
|
171
|
+
// returning early in some cases
|
|
172
|
+
ModifyCodeGenerationFromStringsResult result;
|
|
173
|
+
if (source->IsString() && static_cast<size_t>(source.As<String>()->Length()) > static_cast<size_t>(that.memory_limit / 8)) {
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
result.codegen_allowed = true;
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
auto IsolateEnvironment::CodeGenCallback2(Local<Context> context, Local<Value> source, bool) -> ModifyCodeGenerationFromStringsResult {
|
|
181
|
+
return CodeGenCallback(context, source);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
void IsolateEnvironment::MarkSweepCompactEpilogue(Isolate* isolate, GCType /*gc_type*/, GCCallbackFlags gc_flags, void* data) {
|
|
185
|
+
auto* that = static_cast<IsolateEnvironment*>(data);
|
|
186
|
+
HeapStatistics heap;
|
|
187
|
+
that->isolate->GetHeapStatistics(&heap);
|
|
188
|
+
size_t total_memory = heap.used_heap_size() + that->extra_allocated_memory;
|
|
189
|
+
size_t memory_limit = that->memory_limit + that->misc_memory_size;
|
|
190
|
+
if (total_memory > memory_limit) {
|
|
191
|
+
if ((gc_flags & (GCCallbackFlags::kGCCallbackFlagCollectAllAvailableGarbage | GCCallbackFlags::kGCCallbackFlagForced)) == 0) {
|
|
192
|
+
// Force full garbage collection
|
|
193
|
+
that->RequestMemoryPressureNotification(MemoryPressureLevel::kCritical);
|
|
194
|
+
} else {
|
|
195
|
+
that->Terminate();
|
|
196
|
+
that->hit_memory_limit = true;
|
|
197
|
+
}
|
|
198
|
+
} else if ((gc_flags & GCCallbackFlags::kGCCallbackFlagCollectAllAvailableGarbage) == 0) {
|
|
199
|
+
if (that->did_adjust_heap_limit) {
|
|
200
|
+
// There is also `AutomaticallyRestoreInitialHeapLimit` introduced in v8 7.3.411 / 93283bf04
|
|
201
|
+
// but it seems less effective than this ratcheting strategy.
|
|
202
|
+
isolate->RemoveNearHeapLimitCallback(NearHeapLimitCallback, that->memory_limit);
|
|
203
|
+
isolate->AddNearHeapLimitCallback(NearHeapLimitCallback, data);
|
|
204
|
+
HeapStatistics heap;
|
|
205
|
+
that->isolate->GetHeapStatistics(&heap);
|
|
206
|
+
if (heap.heap_size_limit() == that->initial_heap_size_limit) {
|
|
207
|
+
that->did_adjust_heap_limit = false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (total_memory + total_memory / 4 > memory_limit) {
|
|
211
|
+
// Send "moderate" pressure at 80%
|
|
212
|
+
that->RequestMemoryPressureNotification(MemoryPressureLevel::kModerate);
|
|
213
|
+
} else {
|
|
214
|
+
that->RequestMemoryPressureNotification(MemoryPressureLevel::kNone);
|
|
215
|
+
}
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
auto IsolateEnvironment::NearHeapLimitCallback(void* data, size_t current_heap_limit, size_t /*initial_heap_limit*/) -> size_t {
|
|
221
|
+
// This callback will temporarily give the v8 vm up to an extra 1 GB of memory to prevent the
|
|
222
|
+
// application from crashing.
|
|
223
|
+
auto* that = static_cast<IsolateEnvironment*>(data);
|
|
224
|
+
that->did_adjust_heap_limit = true;
|
|
225
|
+
HeapStatistics heap;
|
|
226
|
+
that->isolate->GetHeapStatistics(&heap);
|
|
227
|
+
if (heap.used_heap_size() + that->extra_allocated_memory > that->memory_limit + that->misc_memory_size) {
|
|
228
|
+
that->RequestMemoryPressureNotification(MemoryPressureLevel::kCritical, true);
|
|
229
|
+
} else {
|
|
230
|
+
that->RequestMemoryPressureNotification(MemoryPressureLevel::kModerate, true);
|
|
231
|
+
}
|
|
232
|
+
return current_heap_limit + 1024 * 1024 * 1024;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
void IsolateEnvironment::RequestMemoryPressureNotification(MemoryPressureLevel memory_pressure, bool as_interrupt) {
|
|
236
|
+
this->memory_pressure = memory_pressure;
|
|
237
|
+
if (as_interrupt) {
|
|
238
|
+
if (memory_pressure > last_memory_pressure) {
|
|
239
|
+
isolate->RequestInterrupt(MemoryPressureInterrupt, static_cast<void*>(this));
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
CheckMemoryPressure();
|
|
243
|
+
if (memory_pressure == MemoryPressureLevel::kCritical) {
|
|
244
|
+
// Reentrant GC doesn't trigger callbacks
|
|
245
|
+
MarkSweepCompactEpilogue(isolate, GCType::kGCTypeMarkSweepCompact, GCCallbackFlags::kGCCallbackFlagForced, reinterpret_cast<void*>(this));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
void IsolateEnvironment::MemoryPressureInterrupt(Isolate* /*isolate*/, void* data) {
|
|
251
|
+
static_cast<IsolateEnvironment*>(data)->CheckMemoryPressure();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
void IsolateEnvironment::CheckMemoryPressure() {
|
|
255
|
+
if (memory_pressure != last_memory_pressure) {
|
|
256
|
+
if (memory_pressure > last_memory_pressure) {
|
|
257
|
+
isolate->MemoryPressureNotification(memory_pressure);
|
|
258
|
+
}
|
|
259
|
+
last_memory_pressure = memory_pressure;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
void IsolateEnvironment::AsyncEntry() {
|
|
264
|
+
Executor::Lock lock(*this);
|
|
265
|
+
if (!nodejs_isolate) {
|
|
266
|
+
// Set v8 stack limit on non-default isolate. This is only needed on non-default threads while
|
|
267
|
+
// on OS X because it allocates just 512kb for each pthread stack, instead of 2mb on other
|
|
268
|
+
// systems. 512kb is lower than the default v8 stack size so JS stack overflows result in
|
|
269
|
+
// segfaults.
|
|
270
|
+
thread_local void* stack_base = GetStackBase();
|
|
271
|
+
if (stack_base != nullptr) {
|
|
272
|
+
// Add 24kb of padding for native code to run
|
|
273
|
+
isolate->SetStackLimit(reinterpret_cast<uintptr_t>(reinterpret_cast<char*>(stack_base) + 1024 * 24));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
while (true) {
|
|
278
|
+
std::queue<unique_ptr<Runnable>> tasks;
|
|
279
|
+
std::queue<unique_ptr<Runnable>> handle_tasks;
|
|
280
|
+
std::queue<unique_ptr<Runnable>> interrupts;
|
|
281
|
+
{
|
|
282
|
+
// Grab current tasks
|
|
283
|
+
auto lock = scheduler->Lock();
|
|
284
|
+
tasks = ExchangeDefault(lock->tasks);
|
|
285
|
+
handle_tasks = ExchangeDefault(lock->handle_tasks);
|
|
286
|
+
interrupts = ExchangeDefault(lock->interrupts);
|
|
287
|
+
if (tasks.empty() && handle_tasks.empty() && interrupts.empty()) {
|
|
288
|
+
lock->DoneRunning();
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Execute interrupt tasks
|
|
294
|
+
while (!interrupts.empty()) {
|
|
295
|
+
interrupts.front()->Run();
|
|
296
|
+
interrupts.pop();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Execute handle tasks
|
|
300
|
+
while (!handle_tasks.empty()) {
|
|
301
|
+
handle_tasks.front()->Run();
|
|
302
|
+
handle_tasks.pop();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Execute tasks
|
|
306
|
+
while (!tasks.empty()) {
|
|
307
|
+
tasks.front()->Run();
|
|
308
|
+
tasks.pop();
|
|
309
|
+
if (terminated) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
CheckMemoryPressure();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
template <std::queue<std::unique_ptr<Runnable>> Scheduler::*Tasks>
|
|
318
|
+
void IsolateEnvironment::InterruptEntryImplementation() {
|
|
319
|
+
// Executor::Lock is already acquired
|
|
320
|
+
while (true) {
|
|
321
|
+
auto interrupts = [&]() {
|
|
322
|
+
return ExchangeDefault((*scheduler->Lock()).*Tasks);
|
|
323
|
+
}();
|
|
324
|
+
if (interrupts.empty()) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
do {
|
|
328
|
+
interrupts.front()->Run();
|
|
329
|
+
interrupts.pop();
|
|
330
|
+
} while (!interrupts.empty());
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
void IsolateEnvironment::InterruptEntryAsync() {
|
|
335
|
+
return InterruptEntryImplementation<&Scheduler::interrupts>();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
void IsolateEnvironment::InterruptEntrySync() {
|
|
339
|
+
return InterruptEntryImplementation<&Scheduler::sync_interrupts>();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
IsolateEnvironment::IsolateEnvironment() :
|
|
343
|
+
owned_isolates{std::make_unique<OwnedIsolates>()},
|
|
344
|
+
scheduler{in_place<UvScheduler>{}, *this},
|
|
345
|
+
executor{*this},
|
|
346
|
+
nodejs_isolate{true} {}
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
IsolateEnvironment::IsolateEnvironment(UvScheduler& default_scheduler) :
|
|
350
|
+
scheduler{in_place<IsolatedScheduler>{}, *this, default_scheduler},
|
|
351
|
+
executor{*this} {}
|
|
352
|
+
|
|
353
|
+
void IsolateEnvironment::IsolateCtor(Isolate* isolate, Local<Context> context) {
|
|
354
|
+
this->isolate = isolate;
|
|
355
|
+
default_context.Reset(isolate, context);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
void IsolateEnvironment::IsolateCtor(size_t memory_limit_in_mb, shared_ptr<v8::BackingStore> snapshot_blob, size_t snapshot_length) {
|
|
359
|
+
memory_limit = memory_limit_in_mb * 1024 * 1024;
|
|
360
|
+
allocator_ptr = std::make_shared<LimitedAllocator>(*this, memory_limit);
|
|
361
|
+
snapshot_blob_ptr = std::move(snapshot_blob);
|
|
362
|
+
|
|
363
|
+
// Calculate resource constraints
|
|
364
|
+
ResourceConstraints rc;
|
|
365
|
+
size_t young_space_in_kb = (size_t)std::pow(2, std::min(sizeof(void*) >= 8 ? 4.0 : 3.0, memory_limit_in_mb / 128.0) + 10);
|
|
366
|
+
size_t old_generation_size_in_mb = memory_limit_in_mb;
|
|
367
|
+
// TODO: Give `ConfigureDefaultsFromHeapSize` a try
|
|
368
|
+
rc.set_max_young_generation_size_in_bytes(young_space_in_kb * 1024);
|
|
369
|
+
rc.set_max_old_generation_size_in_bytes(old_generation_size_in_mb * 1024 * 1024);
|
|
370
|
+
|
|
371
|
+
// Build isolate from create params
|
|
372
|
+
Isolate::CreateParams create_params;
|
|
373
|
+
create_params.constraints = rc;
|
|
374
|
+
create_params.array_buffer_allocator_shared = allocator_ptr;
|
|
375
|
+
if (snapshot_blob_ptr) {
|
|
376
|
+
create_params.snapshot_blob = &startup_data;
|
|
377
|
+
startup_data.data = reinterpret_cast<char*>(snapshot_blob_ptr->Data());
|
|
378
|
+
startup_data.raw_size = snapshot_length;
|
|
379
|
+
}
|
|
380
|
+
task_runner = std::make_shared<IsolateTaskRunner>(holder.lock()->GetIsolate());
|
|
381
|
+
{
|
|
382
|
+
std::lock_guard allocator_lock{isolate_allocator_mutex};
|
|
383
|
+
isolate = Isolate::Allocate();
|
|
384
|
+
PlatformDelegate::RegisterIsolate(isolate, &*scheduler);
|
|
385
|
+
}
|
|
386
|
+
Isolate::Initialize(isolate, create_params);
|
|
387
|
+
|
|
388
|
+
// Various callbacks
|
|
389
|
+
isolate->SetOOMErrorHandler(OOMErrorCallback);
|
|
390
|
+
isolate->SetPromiseRejectCallback(PromiseRejectCallback);
|
|
391
|
+
isolate->SetModifyCodeGenerationFromStringsCallback(CodeGenCallback2);
|
|
392
|
+
|
|
393
|
+
// Add GC callbacks
|
|
394
|
+
isolate->AddGCEpilogueCallback(MarkSweepCompactEpilogue, static_cast<void*>(this), GCType::kGCTypeMarkSweepCompact);
|
|
395
|
+
isolate->AddNearHeapLimitCallback(NearHeapLimitCallback, static_cast<void*>(this));
|
|
396
|
+
|
|
397
|
+
// Heap statistics crushes down lots of different memory spaces into a single number. We note the
|
|
398
|
+
// difference between the requested old space and v8's calculated heap size.
|
|
399
|
+
HeapStatistics heap;
|
|
400
|
+
isolate->GetHeapStatistics(&heap);
|
|
401
|
+
initial_heap_size_limit = heap.heap_size_limit();
|
|
402
|
+
misc_memory_size = heap.heap_size_limit() - memory_limit_in_mb * 1024 * 1024;
|
|
403
|
+
|
|
404
|
+
// Create a default context for the library to use if needed
|
|
405
|
+
{
|
|
406
|
+
Locker locker(isolate);
|
|
407
|
+
HandleScope handle_scope(isolate);
|
|
408
|
+
default_context.Reset(isolate, NewContext());
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// There is no asynchronous Isolate ctor so we should throw away thread specifics in case
|
|
412
|
+
// the client always uses async methods
|
|
413
|
+
isolate->DiscardThreadSpecificMetadata();
|
|
414
|
+
|
|
415
|
+
// Save reference to this isolate in the default isolate
|
|
416
|
+
Executor::GetDefaultEnvironment().owned_isolates->write()->insert({ dispose_wait, holder });
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
IsolateEnvironment::~IsolateEnvironment() {
|
|
420
|
+
if (nodejs_isolate) {
|
|
421
|
+
// Throw away all owned isolates when the root one dies
|
|
422
|
+
auto isolates = *owned_isolates->read(); // copy
|
|
423
|
+
for (const auto& handle : isolates) {
|
|
424
|
+
auto ref = handle.holder.lock();
|
|
425
|
+
if (ref) {
|
|
426
|
+
ref->Dispose();
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
for (const auto& handle : isolates) {
|
|
430
|
+
handle.dispose_wait->Join();
|
|
431
|
+
}
|
|
432
|
+
} else {
|
|
433
|
+
{
|
|
434
|
+
// Grab local pointer to inspector agent with scheduler lock active
|
|
435
|
+
auto agent_ptr = [&]() {
|
|
436
|
+
auto lock = scheduler->Lock();
|
|
437
|
+
return std::move(inspector_agent);
|
|
438
|
+
}();
|
|
439
|
+
// Now activate executor lock and invoke inspector agent's dtor
|
|
440
|
+
Executor::Lock lock{*this};
|
|
441
|
+
agent_ptr.reset();
|
|
442
|
+
// Kill all weak persistents
|
|
443
|
+
for (auto it = weak_persistents.begin(); it != weak_persistents.end(); ) {
|
|
444
|
+
void(*fn)(void*) = it->second.first;
|
|
445
|
+
void* param = it->second.second;
|
|
446
|
+
++it;
|
|
447
|
+
fn(param);
|
|
448
|
+
}
|
|
449
|
+
assert(weak_persistents.empty());
|
|
450
|
+
unhandled_promise_rejections.clear();
|
|
451
|
+
// Destroy outstanding tasks. Do this here while the executor lock is up.
|
|
452
|
+
auto scheduler_lock = scheduler->Lock();
|
|
453
|
+
ExchangeDefault(scheduler_lock->interrupts);
|
|
454
|
+
ExchangeDefault(scheduler_lock->sync_interrupts);
|
|
455
|
+
ExchangeDefault(scheduler_lock->handle_tasks);
|
|
456
|
+
ExchangeDefault(scheduler_lock->tasks);
|
|
457
|
+
}
|
|
458
|
+
{
|
|
459
|
+
std::lock_guard allocator_lock{isolate_allocator_mutex};
|
|
460
|
+
{
|
|
461
|
+
// Dispose() will call destructors for external strings and array buffers, so this lock sets the
|
|
462
|
+
// "current" isolate for those C++ dtors to function correctly without locking v8
|
|
463
|
+
Executor::Scope lock{*this};
|
|
464
|
+
isolate->Dispose();
|
|
465
|
+
}
|
|
466
|
+
// Unregister from Platform
|
|
467
|
+
PlatformDelegate::UnregisterIsolate(isolate);
|
|
468
|
+
}
|
|
469
|
+
// Unreference from default isolate
|
|
470
|
+
executor.default_executor.env.owned_isolates->write()->erase({ dispose_wait, holder });
|
|
471
|
+
}
|
|
472
|
+
// Send notification that this isolate is totally disposed
|
|
473
|
+
dispose_wait->IsolateDidDispose();
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
static void DeserializeInternalFieldsCallback(Local<Object> /*holder*/, int /*index*/, StartupData /*payload*/, void* /*data*/) {
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
auto IsolateEnvironment::NewContext() -> Local<Context> {
|
|
480
|
+
auto context =
|
|
481
|
+
Context::New(isolate, nullptr, {}, {}, &DeserializeInternalFieldsCallback);
|
|
482
|
+
context->AllowCodeGenerationFromStrings(false);
|
|
483
|
+
// TODO (but I'm not going to do it): This causes a DCHECK failure in debug builds. Tested nodejs
|
|
484
|
+
// v14.17.3 & v16.5.1.
|
|
485
|
+
// context->SetErrorMessageForCodeGenerationFromStrings(StringTable::Get().codeGenerationError);
|
|
486
|
+
return context;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
auto IsolateEnvironment::TaskEpilogue() -> std::unique_ptr<ExternalCopy> {
|
|
490
|
+
isolate->PerformMicrotaskCheckpoint();
|
|
491
|
+
CheckMemoryPressure();
|
|
492
|
+
if (hit_memory_limit) {
|
|
493
|
+
throw FatalRuntimeError("Isolate was disposed during execution due to memory limit");
|
|
494
|
+
}
|
|
495
|
+
auto rejected_promises = std::exchange(unhandled_promise_rejections, {});
|
|
496
|
+
for (auto& handle : rejected_promises) {
|
|
497
|
+
if (!handle.IsEmpty()) {
|
|
498
|
+
Context::Scope context_scope{DefaultContext()};
|
|
499
|
+
return ExternalCopy::CopyThrownValue(Deref(handle)->Result());
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return {};
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
auto IsolateEnvironment::GetLimitedAllocator() const -> LimitedAllocator* {
|
|
506
|
+
if (nodejs_isolate) {
|
|
507
|
+
return nullptr;
|
|
508
|
+
} else {
|
|
509
|
+
return static_cast<LimitedAllocator*>(allocator_ptr.get());
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
void IsolateEnvironment::EnableInspectorAgent() {
|
|
514
|
+
inspector_agent = std::make_unique<InspectorAgent>(*this);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
auto IsolateEnvironment::GetInspectorAgent() const -> InspectorAgent* {
|
|
518
|
+
return inspector_agent.get();
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
auto IsolateEnvironment::GetCpuProfileManager() -> CpuProfileManager* {
|
|
522
|
+
if (!cpu_profile_manager) {
|
|
523
|
+
cpu_profile_manager = std::make_shared<CpuProfileManager>();
|
|
524
|
+
}
|
|
525
|
+
return cpu_profile_manager.get();
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
auto IsolateEnvironment::GetCpuTime() -> std::chrono::nanoseconds {
|
|
529
|
+
std::lock_guard<std::mutex> lock(executor.timer_mutex);
|
|
530
|
+
std::chrono::nanoseconds time = executor.cpu_time;
|
|
531
|
+
if (executor.cpu_timer != nullptr) {
|
|
532
|
+
time += executor.cpu_timer->Delta(lock);
|
|
533
|
+
}
|
|
534
|
+
return time;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
auto IsolateEnvironment::GetWallTime() -> std::chrono::nanoseconds {
|
|
538
|
+
std::lock_guard<std::mutex> lock(executor.timer_mutex);
|
|
539
|
+
std::chrono::nanoseconds time = executor.wall_time;
|
|
540
|
+
if (executor.wall_timer != nullptr) {
|
|
541
|
+
time += executor.wall_timer->Delta(lock);
|
|
542
|
+
}
|
|
543
|
+
return time;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
void IsolateEnvironment::Terminate() {
|
|
547
|
+
assert(!nodejs_isolate);
|
|
548
|
+
terminated = true;
|
|
549
|
+
|
|
550
|
+
// Destroy inspector session
|
|
551
|
+
{
|
|
552
|
+
auto lock = scheduler->Lock();
|
|
553
|
+
lock->CancelAsync();
|
|
554
|
+
if (inspector_agent) {
|
|
555
|
+
inspector_agent->Terminate();
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Request interrupt to ensure execution is interrupted in race conditions
|
|
560
|
+
isolate->RequestInterrupt([](Isolate* isolate, void* /* param */) {
|
|
561
|
+
isolate->AddBeforeCallEnteredCallback([](Isolate* isolate) {
|
|
562
|
+
isolate->TerminateExecution();
|
|
563
|
+
});
|
|
564
|
+
isolate->TerminateExecution();
|
|
565
|
+
}, nullptr);
|
|
566
|
+
|
|
567
|
+
// Throw away Holder reference
|
|
568
|
+
auto ref = holder.lock();
|
|
569
|
+
if (ref) {
|
|
570
|
+
ref->isolate.write()->reset();
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
void IsolateEnvironment::AddWeakCallback(Persistent<Value>* handle, void(*fn)(void*), void* param) {
|
|
575
|
+
if (nodejs_isolate) {
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
auto it = weak_persistents.find(handle);
|
|
579
|
+
if (it != weak_persistents.end()) {
|
|
580
|
+
throw std::logic_error("Weak callback already added");
|
|
581
|
+
}
|
|
582
|
+
weak_persistents.insert(std::make_pair(handle, std::make_pair(fn, param)));
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
void IsolateEnvironment::RemoveWeakCallback(Persistent<Value>* handle) {
|
|
586
|
+
if (nodejs_isolate) {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
auto it = weak_persistents.find(handle);
|
|
590
|
+
if (it == weak_persistents.end()) {
|
|
591
|
+
throw std::logic_error("Weak callback doesn't exist");
|
|
592
|
+
}
|
|
593
|
+
weak_persistents.erase(it);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
void AdjustRemotes(int delta) {
|
|
597
|
+
IsolateEnvironment::GetCurrent().AdjustRemotes(delta);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
auto RaiseCatastrophicError(RemoteHandle<Function>& handler, const char* message) -> bool {
|
|
601
|
+
if (!handler) {
|
|
602
|
+
return false;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
class ErrorTask : public Task {
|
|
606
|
+
public:
|
|
607
|
+
explicit ErrorTask(const char* message, RemoteHandle<Function> handler) :
|
|
608
|
+
message{message}, handler{std::move(handler)} {}
|
|
609
|
+
|
|
610
|
+
void Run() final {
|
|
611
|
+
auto* isolate = Isolate::GetCurrent();
|
|
612
|
+
auto context = isolate->GetCurrentContext();
|
|
613
|
+
auto fn = handler.Deref();
|
|
614
|
+
Local<Value> argv[] = { HandleCast<Local<String>>(message) };
|
|
615
|
+
Unmaybe(fn->Call(context, Undefined(isolate), 1, argv));
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
private:
|
|
619
|
+
const char* message;
|
|
620
|
+
RemoteHandle<Function> handler;
|
|
621
|
+
};
|
|
622
|
+
handler.GetIsolateHolder()->ScheduleTask(std::make_unique<ErrorTask>(message, handler), false, true);
|
|
623
|
+
return true;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
} // namespace ivm
|