@nocobase/plugin-workflow-javascript 2.1.0-beta.11 → 2.1.0-beta.13

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.
Files changed (107) hide show
  1. package/dist/externalVersion.js +4 -4
  2. package/dist/node_modules/isolated-vm/.clang-tidy +13 -0
  3. package/dist/node_modules/isolated-vm/.dockerignore +9 -0
  4. package/dist/node_modules/isolated-vm/Dockerfile.alpine +9 -0
  5. package/dist/node_modules/isolated-vm/Dockerfile.debian +12 -0
  6. package/dist/node_modules/isolated-vm/LICENSE +13 -0
  7. package/dist/node_modules/isolated-vm/binding.gyp +120 -0
  8. package/dist/node_modules/isolated-vm/include.js +3 -0
  9. package/dist/node_modules/isolated-vm/inspector-example.js +59 -0
  10. package/dist/node_modules/isolated-vm/isolated-vm.d.ts +820 -0
  11. package/dist/node_modules/isolated-vm/isolated-vm.js +1 -0
  12. package/dist/node_modules/isolated-vm/native-example/binding.gyp +23 -0
  13. package/dist/node_modules/isolated-vm/native-example/example.cc +61 -0
  14. package/dist/node_modules/isolated-vm/native-example/package.json +13 -0
  15. package/dist/node_modules/isolated-vm/native-example/usage.js +35 -0
  16. package/dist/node_modules/isolated-vm/out/isolated_vm.node +0 -0
  17. package/dist/node_modules/isolated-vm/package.json +1 -0
  18. package/dist/node_modules/isolated-vm/src/external_copy/error.h +33 -0
  19. package/dist/node_modules/isolated-vm/src/external_copy/external_copy.cc +509 -0
  20. package/dist/node_modules/isolated-vm/src/external_copy/external_copy.h +117 -0
  21. package/dist/node_modules/isolated-vm/src/external_copy/serializer.cc +85 -0
  22. package/dist/node_modules/isolated-vm/src/external_copy/serializer.h +136 -0
  23. package/dist/node_modules/isolated-vm/src/external_copy/serializer_nortti.cc +73 -0
  24. package/dist/node_modules/isolated-vm/src/external_copy/string.cc +124 -0
  25. package/dist/node_modules/isolated-vm/src/external_copy/string.h +28 -0
  26. package/dist/node_modules/isolated-vm/src/isolate/allocator.h +32 -0
  27. package/dist/node_modules/isolated-vm/src/isolate/allocator_nortti.cc +142 -0
  28. package/dist/node_modules/isolated-vm/src/isolate/class_handle.h +334 -0
  29. package/dist/node_modules/isolated-vm/src/isolate/cpu_profile_manager.cc +220 -0
  30. package/dist/node_modules/isolated-vm/src/isolate/cpu_profile_manager.h +100 -0
  31. package/dist/node_modules/isolated-vm/src/isolate/environment.cc +626 -0
  32. package/dist/node_modules/isolated-vm/src/isolate/environment.h +381 -0
  33. package/dist/node_modules/isolated-vm/src/isolate/executor.cc +198 -0
  34. package/dist/node_modules/isolated-vm/src/isolate/executor.h +183 -0
  35. package/dist/node_modules/isolated-vm/src/isolate/external.h +64 -0
  36. package/dist/node_modules/isolated-vm/src/isolate/functor_runners.h +97 -0
  37. package/dist/node_modules/isolated-vm/src/isolate/generic/array.h +145 -0
  38. package/dist/node_modules/isolated-vm/src/isolate/generic/callbacks.h +272 -0
  39. package/dist/node_modules/isolated-vm/src/isolate/generic/error.h +140 -0
  40. package/dist/node_modules/isolated-vm/src/isolate/generic/extract_params.h +145 -0
  41. package/dist/node_modules/isolated-vm/src/isolate/generic/handle_cast.h +257 -0
  42. package/dist/node_modules/isolated-vm/src/isolate/generic/read_option.h +47 -0
  43. package/dist/node_modules/isolated-vm/src/isolate/holder.cc +88 -0
  44. package/dist/node_modules/isolated-vm/src/isolate/holder.h +63 -0
  45. package/dist/node_modules/isolated-vm/src/isolate/inspector.cc +200 -0
  46. package/dist/node_modules/isolated-vm/src/isolate/inspector.h +70 -0
  47. package/dist/node_modules/isolated-vm/src/isolate/node_wrapper.h +15 -0
  48. package/dist/node_modules/isolated-vm/src/isolate/platform_delegate.cc +22 -0
  49. package/dist/node_modules/isolated-vm/src/isolate/platform_delegate.h +46 -0
  50. package/dist/node_modules/isolated-vm/src/isolate/remote_handle.h +164 -0
  51. package/dist/node_modules/isolated-vm/src/isolate/run_with_timeout.h +171 -0
  52. package/dist/node_modules/isolated-vm/src/isolate/runnable.h +29 -0
  53. package/dist/node_modules/isolated-vm/src/isolate/scheduler.cc +191 -0
  54. package/dist/node_modules/isolated-vm/src/isolate/scheduler.h +165 -0
  55. package/dist/node_modules/isolated-vm/src/isolate/specific.h +35 -0
  56. package/dist/node_modules/isolated-vm/src/isolate/stack_trace.cc +219 -0
  57. package/dist/node_modules/isolated-vm/src/isolate/stack_trace.h +24 -0
  58. package/dist/node_modules/isolated-vm/src/isolate/strings.h +127 -0
  59. package/dist/node_modules/isolated-vm/src/isolate/three_phase_task.cc +385 -0
  60. package/dist/node_modules/isolated-vm/src/isolate/three_phase_task.h +136 -0
  61. package/dist/node_modules/isolated-vm/src/isolate/transferable.h +15 -0
  62. package/dist/node_modules/isolated-vm/src/isolate/util.h +45 -0
  63. package/dist/node_modules/isolated-vm/src/isolate/v8_inspector_wrapper.h +12 -0
  64. package/dist/node_modules/isolated-vm/src/isolate/v8_version.h +12 -0
  65. package/dist/node_modules/isolated-vm/src/isolated_vm.h +71 -0
  66. package/dist/node_modules/isolated-vm/src/lib/covariant.h +50 -0
  67. package/dist/node_modules/isolated-vm/src/lib/lockable.h +178 -0
  68. package/dist/node_modules/isolated-vm/src/lib/suspend.h +106 -0
  69. package/dist/node_modules/isolated-vm/src/lib/thread_pool.cc +98 -0
  70. package/dist/node_modules/isolated-vm/src/lib/thread_pool.h +45 -0
  71. package/dist/node_modules/isolated-vm/src/lib/timer.cc +233 -0
  72. package/dist/node_modules/isolated-vm/src/lib/timer.h +36 -0
  73. package/dist/node_modules/isolated-vm/src/module/callback.cc +151 -0
  74. package/dist/node_modules/isolated-vm/src/module/callback.h +64 -0
  75. package/dist/node_modules/isolated-vm/src/module/context_handle.cc +241 -0
  76. package/dist/node_modules/isolated-vm/src/module/context_handle.h +35 -0
  77. package/dist/node_modules/isolated-vm/src/module/evaluation.cc +109 -0
  78. package/dist/node_modules/isolated-vm/src/module/evaluation.h +99 -0
  79. package/dist/node_modules/isolated-vm/src/module/external_copy_handle.cc +119 -0
  80. package/dist/node_modules/isolated-vm/src/module/external_copy_handle.h +64 -0
  81. package/dist/node_modules/isolated-vm/src/module/isolate.cc +136 -0
  82. package/dist/node_modules/isolated-vm/src/module/isolate_handle.cc +611 -0
  83. package/dist/node_modules/isolated-vm/src/module/isolate_handle.h +47 -0
  84. package/dist/node_modules/isolated-vm/src/module/lib_handle.cc +77 -0
  85. package/dist/node_modules/isolated-vm/src/module/lib_handle.h +28 -0
  86. package/dist/node_modules/isolated-vm/src/module/module_handle.cc +475 -0
  87. package/dist/node_modules/isolated-vm/src/module/module_handle.h +68 -0
  88. package/dist/node_modules/isolated-vm/src/module/native_module_handle.cc +104 -0
  89. package/dist/node_modules/isolated-vm/src/module/native_module_handle.h +49 -0
  90. package/dist/node_modules/isolated-vm/src/module/reference_handle.cc +636 -0
  91. package/dist/node_modules/isolated-vm/src/module/reference_handle.h +106 -0
  92. package/dist/node_modules/isolated-vm/src/module/script_handle.cc +107 -0
  93. package/dist/node_modules/isolated-vm/src/module/script_handle.h +37 -0
  94. package/dist/node_modules/isolated-vm/src/module/session_handle.cc +173 -0
  95. package/dist/node_modules/isolated-vm/src/module/session_handle.h +31 -0
  96. package/dist/node_modules/isolated-vm/src/module/transferable.cc +268 -0
  97. package/dist/node_modules/isolated-vm/src/module/transferable.h +42 -0
  98. package/dist/node_modules/isolated-vm/vendor/v8_inspector/nodejs_v18.0.0.h +360 -0
  99. package/dist/node_modules/isolated-vm/vendor/v8_inspector/nodejs_v18.3.0.h +376 -0
  100. package/dist/node_modules/isolated-vm/vendor/v8_inspector/nodejs_v20.0.0.h +397 -0
  101. package/dist/node_modules/isolated-vm/vendor/v8_inspector/nodejs_v22.0.0.h +419 -0
  102. package/dist/node_modules/winston-transport/package.json +1 -1
  103. package/dist/server/IsolatedVm.js +75 -0
  104. package/dist/server/ScriptInstruction.d.ts +6 -0
  105. package/dist/server/ScriptInstruction.js +11 -1
  106. package/dist/server/Vm.js +42 -27
  107. 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