@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,127 @@
1
+ #pragma once
2
+ #include "isolate/generic/error.h"
3
+ #include "isolate/generic/handle_cast.h"
4
+ #include <v8.h>
5
+
6
+ namespace ivm {
7
+
8
+ class StringTable {
9
+ public:
10
+ class String {
11
+ public:
12
+ String(const char* value) : value{value} {} // NOLINT(hicpp-explicit-conversions)
13
+ String(const String&) = delete;
14
+ String(String&&) = delete;
15
+ ~String() = default;
16
+ auto operator=(const String&) = delete;
17
+ auto operator=(String&&) = delete;
18
+
19
+ operator v8::Local<v8::Name>() { // NOLINT(hicpp-explicit-conversions)
20
+ return v8::Local<v8::String>{*this}.As<v8::Name>();
21
+ }
22
+
23
+ operator v8::Local<v8::Value>() { // NOLINT(hicpp-explicit-conversions)
24
+ return v8::Local<v8::String>{*this}.As<v8::Value>();
25
+ }
26
+
27
+ operator v8::Local<v8::String>() { // NOLINT(hicpp-explicit-conversions)
28
+ auto* isolate = v8::Isolate::GetCurrent();
29
+ if (handle.IsEmpty()) {
30
+ auto local = Unmaybe(v8::String::NewFromOneByte(
31
+ isolate, (const uint8_t*)value, v8::NewStringType::kInternalized));
32
+ handle.Set(isolate, local);
33
+ return local;
34
+ } else {
35
+ return handle.Get(isolate);
36
+ }
37
+ }
38
+
39
+ private:
40
+ const char* value;
41
+ v8::Eternal<v8::String> handle;
42
+ };
43
+
44
+ static auto Get() -> auto&;
45
+
46
+ // StringTable::Get().
47
+ String accessors{"accessors"};
48
+ String arguments{"arguments"};
49
+ String async{"async"};
50
+ String boolean{"boolean"};
51
+ String cachedData{"cachedData"};
52
+ String cachedDataRejected{"cachedDataRejected"};
53
+ String code{"code"};
54
+ // String codeGenerationError{"Code generation from large string was denied"};
55
+ String colonSpace{": "};
56
+ String columnOffset{"columnOffset"};
57
+ String copy{"copy"};
58
+ String externalCopy{"externalCopy"};
59
+ String filename{"filename"};
60
+ String function{"function"};
61
+ String global{"global"};
62
+ String ignored{"ignored"};
63
+ String inspector{"inspector"};
64
+ String isolateIsDisposed{"Isolate is disposed"};
65
+ String isolatedVm{"isolated-vm"};
66
+ String length{"length"};
67
+ String lineOffset{"lineOffset"};
68
+ String message{"message"};
69
+ String meta{"meta"};
70
+ String name{"name"};
71
+ String null{"null"};
72
+ String number{"number"};
73
+ String object{"object"};
74
+ String onCatastrophicError{"onCatastrophicError"};
75
+ String produceCachedData{"produceCachedData"};
76
+ String promise{"promise"};
77
+ String reference{"reference"};
78
+ String release{"release"};
79
+ String result{"result"};
80
+ String snapshot{"snapshot"};
81
+ String stack{"stack"};
82
+ String string{"string"};
83
+ String timeout{"timeout"};
84
+ String transferIn{"transferIn"};
85
+ String transferList{"transferList"};
86
+ String transferOut{"transferOut"};
87
+ String undefined{"undefined"};
88
+ String unsafeInherit{"unsafeInherit"};
89
+
90
+ String does_zap_garbage{"does_zap_garbage"};
91
+ String externally_allocated_size{"externally_allocated_size"};
92
+ String heap_size_limit{"heap_size_limit"};
93
+ String malloced_memory{"malloced_memory"};
94
+ String peak_malloced_memory{"peak_malloced_memory"};
95
+ String total_available_size{"total_available_size"};
96
+ String total_heap_size{"total_heap_size"};
97
+ String total_heap_size_executable{"total_heap_size_executable"};
98
+ String total_physical_size{"total_physical_size"};
99
+ String used_heap_size{"used_heap_size"};
100
+
101
+ // CPU Profiler specific keys
102
+ String threadId{"threadId"};
103
+ String profile{"profile"};
104
+ String startTime{"startTime"};
105
+ String endTime{"endTime"};
106
+ String samples{"samples"};
107
+ String timeDeltas{"timeDeltas"};
108
+ String nodes{"nodes"};
109
+ String id{"id"};
110
+ String callFrame{"callFrame"};
111
+ String functionName{"functionName"};
112
+ String scriptId{"scriptId"};
113
+ String url{"url"};
114
+ String lineNumber{"lineNumber"};
115
+ String columnNumber{"columnNumber"};
116
+ String hitCount{"hitCount"};
117
+ String children{"children"};
118
+ String title{"title"};
119
+ String bailoutReason{"bailoutReason"};
120
+ };
121
+
122
+ inline auto HandleCastImpl(
123
+ StringTable::String& value, const HandleCastArguments& /*arguments*/, HandleCastTag<v8::Local<v8::String>> /*tag*/) {
124
+ return v8::Local<v8::String>{value};
125
+ }
126
+
127
+ } // namespace ivm
@@ -0,0 +1,385 @@
1
+ #include "three_phase_task.h"
2
+ #include "external_copy/external_copy.h"
3
+ #include <cstring>
4
+
5
+ using namespace v8;
6
+ using std::unique_ptr;
7
+
8
+ namespace ivm {
9
+
10
+ /**
11
+ * CalleeInfo implementation
12
+ */
13
+ ThreePhaseTask::CalleeInfo::CalleeInfo(
14
+ Local<Promise::Resolver> resolver,
15
+ Local<Context> context,
16
+ Local<StackTrace> stack_trace
17
+ ) : remotes(resolver, context, stack_trace) {
18
+ auto& env = IsolateEnvironment::GetCurrent();
19
+ if (env.IsDefault()) {
20
+ async = node::EmitAsyncInit(env.GetIsolate(), resolver->GetPromise(), StringTable::Get().isolatedVm);
21
+ }
22
+ }
23
+
24
+ ThreePhaseTask::CalleeInfo::CalleeInfo(CalleeInfo&& that) noexcept :
25
+ remotes{std::move(that.remotes)}, async{std::exchange(that.async, {0, 0})} {}
26
+
27
+ ThreePhaseTask::CalleeInfo::~CalleeInfo() {
28
+ auto& env = IsolateEnvironment::GetCurrent();
29
+ node::async_context tmp{0, 0};
30
+ if (env.IsDefault() && std::memcmp(&async, &tmp, sizeof(node::async_context)) != 0) {
31
+ node::EmitAsyncDestroy(env.GetIsolate(), async);
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Wrapper around node's version of the same class which does nothing if this isn't the node
37
+ * isolate.
38
+ *
39
+ * nb: CallbackScope sets up a v8::TryCatch so if you need to catch an exception do this *before*
40
+ * the v8::TryCatch.
41
+ */
42
+ struct CallbackScope {
43
+ unique_ptr<node::CallbackScope> scope;
44
+
45
+ CallbackScope(node::async_context async, Local<Object> resource) {
46
+ auto& env = IsolateEnvironment::GetCurrent();
47
+ if (env.IsDefault()) {
48
+ scope = std::make_unique<node::CallbackScope>(env.GetIsolate(), resource, async);
49
+ }
50
+ }
51
+ };
52
+
53
+ /**
54
+ * Phase2Runner implementation
55
+ */
56
+ ThreePhaseTask::Phase2Runner::Phase2Runner(
57
+ unique_ptr<ThreePhaseTask> self,
58
+ CalleeInfo info
59
+ ) :
60
+ self(std::move(self)),
61
+ info(std::move(info)) {}
62
+
63
+ ThreePhaseTask::Phase2Runner::~Phase2Runner() {
64
+ if (!did_run) {
65
+ // The task never got to run
66
+ struct Phase3Orphan : public Runnable {
67
+ unique_ptr<ThreePhaseTask> self;
68
+ CalleeInfo info;
69
+
70
+ Phase3Orphan(
71
+ unique_ptr<ThreePhaseTask> self,
72
+ CalleeInfo info
73
+ ) :
74
+ self(std::move(self)),
75
+ info(std::move(info)) {}
76
+
77
+ void Run() final {
78
+ // Revive our persistent handles
79
+ Isolate* isolate = Isolate::GetCurrent();
80
+ auto context_local = info.remotes.Deref<1>();
81
+ Context::Scope context_scope(context_local);
82
+ auto promise_local = info.remotes.Deref<0>();
83
+ CallbackScope callback_scope(info.async, promise_local);
84
+ // Throw from promise
85
+ Local<Object> error = Exception::Error(StringTable::Get().isolateIsDisposed).As<Object>();
86
+ StackTraceHolder::AttachStack(error, info.remotes.Deref<2>());
87
+ Unmaybe(promise_local->Reject(context_local, error));
88
+ isolate->PerformMicrotaskCheckpoint();
89
+ }
90
+ };
91
+ // Schedule a throw task back in first isolate
92
+ auto* holder = info.remotes.GetIsolateHolder();
93
+ holder->ScheduleTask(
94
+ std::make_unique<Phase3Orphan>(
95
+ std::move(self),
96
+ std::move(info)
97
+ ), false, true
98
+ );
99
+ }
100
+ }
101
+
102
+ void ThreePhaseTask::Phase2Runner::Run() {
103
+
104
+ // This class will be used if Phase2() throws an error
105
+ struct Phase3Failure : public Runnable {
106
+ unique_ptr<ThreePhaseTask> self;
107
+ CalleeInfo info;
108
+ unique_ptr<ExternalCopy> error;
109
+
110
+ Phase3Failure(
111
+ unique_ptr<ThreePhaseTask> self,
112
+ CalleeInfo info,
113
+ unique_ptr<ExternalCopy> error
114
+ ) :
115
+ self(std::move(self)),
116
+ info(std::move(info)),
117
+ error(std::move(error)) {}
118
+
119
+ void Run() final {
120
+ // Revive our persistent handles
121
+ Isolate* isolate = Isolate::GetCurrent();
122
+ auto context_local = info.remotes.Deref<1>();
123
+ Context::Scope context_scope(context_local);
124
+ auto promise_local = info.remotes.Deref<0>();
125
+ CallbackScope callback_scope(info.async, promise_local);
126
+ Local<Value> rejection;
127
+ if (error) {
128
+ rejection = error->CopyInto();
129
+ } else {
130
+ rejection = Exception::Error(v8_string("An exception was thrown. Sorry I don't know more."));
131
+ }
132
+ if (rejection->IsObject()) {
133
+ StackTraceHolder::ChainStack(rejection.As<Object>(), info.remotes.Deref<2>());
134
+ }
135
+ // If Reject fails then I think that's bad..
136
+ Unmaybe(promise_local->Reject(context_local, rejection));
137
+ isolate->PerformMicrotaskCheckpoint();
138
+ }
139
+ };
140
+
141
+ // This is called if Phase2() does not throw
142
+ struct Phase3Success : public Runnable {
143
+ unique_ptr<ThreePhaseTask> self;
144
+ CalleeInfo info;
145
+
146
+ Phase3Success(
147
+ unique_ptr<ThreePhaseTask> self,
148
+ CalleeInfo info
149
+ ) :
150
+ self(std::move(self)),
151
+ info(std::move(info)) {}
152
+
153
+ void Run() final {
154
+ Isolate* isolate = Isolate::GetCurrent();
155
+ auto context_local = info.remotes.Deref<1>();
156
+ Context::Scope context_scope(context_local);
157
+ auto promise_local = info.remotes.Deref<0>();
158
+ CallbackScope callback_scope(info.async, promise_local);
159
+ FunctorRunners::RunCatchValue([&]() {
160
+ // Final callback
161
+ Unmaybe(promise_local->Resolve(context_local, self->Phase3()));
162
+ }, [&](Local<Value> error) {
163
+ // Error was thrown
164
+ if (error->IsObject()) {
165
+ StackTraceHolder::AttachStack(error.As<Object>(), info.remotes.Deref<2>());
166
+ }
167
+ Unmaybe(promise_local->Reject(context_local, error));
168
+ });
169
+ isolate->PerformMicrotaskCheckpoint();
170
+ }
171
+ };
172
+
173
+ did_run = true;
174
+ auto schedule_error = [&](std::unique_ptr<ExternalCopy> error) {
175
+ // Schedule a task to enter the first isolate so we can throw the error at the promise
176
+ auto* holder = info.remotes.GetIsolateHolder();
177
+ holder->ScheduleTask(std::make_unique<Phase3Failure>(std::move(self), std::move(info), std::move(error)), false, true);
178
+ };
179
+ FunctorRunners::RunCatchExternal(IsolateEnvironment::GetCurrent().DefaultContext(), [&]() {
180
+ // Continue the task
181
+ self->Phase2();
182
+ auto epilogue_error = IsolateEnvironment::GetCurrent().TaskEpilogue();
183
+ if (epilogue_error) {
184
+ schedule_error(std::move(epilogue_error));
185
+ } else {
186
+ auto* holder = info.remotes.GetIsolateHolder();
187
+ holder->ScheduleTask(std::make_unique<Phase3Success>(std::move(self), std::move(info)), false, true);
188
+ }
189
+ }, schedule_error);
190
+ }
191
+
192
+ /**
193
+ * Phase2RunnerIgnored implementation
194
+ */
195
+ ThreePhaseTask::Phase2RunnerIgnored::Phase2RunnerIgnored(unique_ptr<ThreePhaseTask> self) : self(std::move(self)) {}
196
+
197
+ void ThreePhaseTask::Phase2RunnerIgnored::Run() {
198
+ TryCatch try_catch{Isolate::GetCurrent()};
199
+ try {
200
+ self->Phase2();
201
+ IsolateEnvironment::GetCurrent().TaskEpilogue();
202
+ } catch (const RuntimeError& cc_error) {}
203
+ }
204
+
205
+ /**
206
+ * RunSync implementation
207
+ */
208
+ auto ThreePhaseTask::RunSync(IsolateHolder& second_isolate, bool allow_async) -> Local<Value> {
209
+ // Grab a reference to second isolate
210
+ auto second_isolate_ref = second_isolate.GetIsolate();
211
+ if (!second_isolate_ref) {
212
+ throw RuntimeGenericError("Isolated is disposed");
213
+ }
214
+ if (second_isolate_ref->GetIsolate() == Isolate::GetCurrent()) {
215
+ if (allow_async) {
216
+ throw RuntimeGenericError("This function may not be called from the default thread");
217
+ }
218
+ // Shortcut when calling a sync method belonging to the currently entered isolate. This avoids
219
+ // the deadlock protection below
220
+ Phase2();
221
+ second_isolate_ref->CheckMemoryPressure();
222
+
223
+ } else {
224
+
225
+ bool is_recursive = Locker::IsLocked(second_isolate_ref->GetIsolate());
226
+ if (Executor::IsDefaultThread() || is_recursive) {
227
+ if (allow_async) {
228
+ throw RuntimeGenericError("This function may not be called from the default thread");
229
+ }
230
+
231
+ // Helper function which flushes handle tasks
232
+ auto run_handle_tasks = [](IsolateEnvironment& env) {
233
+ auto handle_tasks = [&]() {
234
+ return ExchangeDefault(env.scheduler->Lock()->handle_tasks);
235
+ }();
236
+ if (handle_tasks.empty()) {
237
+ return;
238
+ }
239
+ do {
240
+ handle_tasks.front()->Run();
241
+ handle_tasks.pop();
242
+ } while (!handle_tasks.empty());
243
+ };
244
+
245
+ // This is the simple sync runner case
246
+ unique_ptr<ExternalCopy> error;
247
+ {
248
+ Executor::Lock lock(*second_isolate_ref);
249
+
250
+ // Run handle tasks first
251
+ run_handle_tasks(*second_isolate_ref);
252
+
253
+ // Now run the actual work
254
+ FunctorRunners::RunCatchExternal(second_isolate_ref->DefaultContext(), [&]() {
255
+ // Run Phase2 and externalize errors
256
+ Phase2();
257
+ if (!is_recursive) {
258
+ error = second_isolate_ref->TaskEpilogue();
259
+ }
260
+ }, [&](unique_ptr<ExternalCopy> error_inner) {
261
+
262
+ // We need to stash the error in the outer unique_ptr because the executor lock is still up
263
+ error = std::move(error_inner);
264
+ });
265
+ }
266
+
267
+ // Run handle tasks for default isolate now
268
+ auto& current_env = IsolateEnvironment::GetCurrent();
269
+ if (current_env.IsDefault()) {
270
+ run_handle_tasks(current_env);
271
+ }
272
+
273
+ if (error) {
274
+ // Throw to outer isolate
275
+ Isolate* isolate = Isolate::GetCurrent();
276
+ Local<Value> error_copy = error->CopyInto();
277
+ if (error_copy->IsObject()) {
278
+ StackTraceHolder::ChainStack(error_copy.As<Object>(), StackTrace::CurrentStackTrace(isolate, 10));
279
+ }
280
+ isolate->ThrowException(error_copy);
281
+ throw RuntimeError();
282
+ }
283
+
284
+ } else if (second_isolate_ref->IsDefault()) {
285
+
286
+ // In this case we asyncronously call the default thread and suspend this thread
287
+ struct AsyncRunner final : public Runnable {
288
+ bool allow_async = false;
289
+ bool did_run = false;
290
+ ThreePhaseTask& self;
291
+ Scheduler::AsyncWait& wait;
292
+ lockable_t<bool, false, true>& done;
293
+ unique_ptr<ExternalCopy>& error;
294
+
295
+ AsyncRunner(
296
+ ThreePhaseTask& self,
297
+ Scheduler::AsyncWait& wait,
298
+ lockable_t<bool, false, true>& done,
299
+ bool allow_async,
300
+ unique_ptr<ExternalCopy>& error
301
+ ) : allow_async{allow_async}, self{self}, wait{wait}, done{done}, error{error} {}
302
+
303
+ AsyncRunner(const AsyncRunner&) = delete;
304
+ auto operator=(const AsyncRunner&) -> AsyncRunner& = delete;
305
+
306
+ ~AsyncRunner() final {
307
+ if (!did_run) {
308
+ error = std::make_unique<ExternalCopyError>(ExternalCopyError::ErrorType::Error, "Isolate is disposed");
309
+ }
310
+ // nb: The lock must be held while invoking `notify_one` since the condition variable will
311
+ // be destroyed immediately following the wake.
312
+ auto lock = done.write();
313
+ *lock = true;
314
+ done.notify_one();
315
+ }
316
+
317
+ void Run() final {
318
+ did_run = true;
319
+ FunctorRunners::RunCatchExternal(IsolateEnvironment::GetCurrent().DefaultContext(), [ this ]() {
320
+ // Now in the default thread
321
+ const auto is_async = [&]() {
322
+ if (allow_async) {
323
+ return self.Phase2Async(wait);
324
+ } else {
325
+ self.Phase2();
326
+ return false;
327
+ }
328
+ }();
329
+ if (!is_async) {
330
+ wait.Done();
331
+ }
332
+ this->error = IsolateEnvironment::GetCurrent().TaskEpilogue();
333
+ }, [ this ](unique_ptr<ExternalCopy> error) {
334
+ this->error = std::move(error);
335
+ wait.Done();
336
+ });
337
+ }
338
+ };
339
+
340
+ Isolate* isolate = Isolate::GetCurrent();
341
+ unique_ptr<ExternalCopy> error;
342
+ {
343
+ // Setup condition variable to sleep this thread
344
+ IsolateEnvironment& env = IsolateEnvironment::GetCurrent();
345
+ Scheduler::AsyncWait wait(*env.scheduler);
346
+ lockable_t<bool, false, true> done{false};
347
+ // Scope to unlock v8 in this thread and set up the wait
348
+ Executor::Unlock unlocker(env);
349
+ // Run it and sleep
350
+ second_isolate.ScheduleTask(std::make_unique<AsyncRunner>(*this, wait, done, allow_async, error), false, true);
351
+ // Wait for AsyncRunner to finish
352
+ auto lock = done.read<true>();
353
+ while (!*lock) {
354
+ lock.wait();
355
+ }
356
+ // Wait for `applySyncPromise` to finish
357
+ if (wait.Wait() == Scheduler::AsyncWait::canceled) {
358
+ throw RuntimeGenericError("Isolate is disposed");
359
+ }
360
+ }
361
+
362
+ // At this point thread synchronization is done and Phase2() has finished
363
+ if (error) {
364
+ Local<Value> error_copy = error->CopyInto();
365
+ if (error_copy->IsObject()) {
366
+ StackTraceHolder::ChainStack(error_copy.As<Object>(), StackTrace::CurrentStackTrace(isolate, 10));
367
+ }
368
+ isolate->ThrowException(error_copy);
369
+ throw RuntimeError();
370
+ }
371
+
372
+ } else {
373
+
374
+ // ~ very specific error message ~
375
+ throw RuntimeGenericError(
376
+ "Calling a synchronous isolated-vm function on a non-default isolate from within an asynchronous isolated-vm function is not allowed."
377
+ );
378
+ }
379
+ }
380
+
381
+ // Final phase
382
+ return Phase3();
383
+ }
384
+
385
+ } // namespace ivm
@@ -0,0 +1,136 @@
1
+ #pragma once
2
+ #include "node_wrapper.h"
3
+ #include "environment.h"
4
+ #include "holder.h"
5
+ #include "functor_runners.h"
6
+ #include "remote_handle.h"
7
+ #include "stack_trace.h"
8
+ #include "util.h"
9
+ #include <memory>
10
+
11
+ namespace ivm {
12
+
13
+ /**
14
+ * Most operations in this library can be decomposed into three phases.
15
+ *
16
+ * - Phase 1 [Isolate 1]: copy data out of current isolate
17
+ * - Phase 2 [Isolate 2]: copy data into new isolate, run work, copy data out of isolate
18
+ * - Phase 3 [Isolate 1]: copy results from phase 2 into the original isolate
19
+ *
20
+ * This class handles the locking and thread synchronization for either synchronous or
21
+ * asynchronous functions. That way the same code can be used for both versions of each function.
22
+ *
23
+ * These runners are invoked via: ThreePhaseTask::Run<async, T>(isolate, args...);
24
+ *
25
+ * Where:
26
+ * async = 0 -- Synchronous execution
27
+ * async = 1 -- Asynchronous execution, promise returned
28
+ * async = 2 -- Asynchronous execution, result ignored (Phase3() is never called)
29
+ * async = 4 -- Synchronous + asyncronous, original thread waits for async Phase2()
30
+ */
31
+ class ThreePhaseTask {
32
+ private:
33
+ /**
34
+ * Contains references back to the original isolate which will be used after phase 2 to wake the
35
+ * isolate up and begin phase 3
36
+ */
37
+ struct CalleeInfo {
38
+ RemoteTuple<v8::Promise::Resolver, v8::Context, v8::StackTrace> remotes;
39
+ node::async_context async {0, 0};
40
+ CalleeInfo(
41
+ v8::Local<v8::Promise::Resolver> resolver,
42
+ v8::Local<v8::Context> context,
43
+ v8::Local<v8::StackTrace> stack_trace
44
+ );
45
+ CalleeInfo(const CalleeInfo&) = delete;
46
+ CalleeInfo(CalleeInfo&& /*that*/) noexcept;
47
+ auto operator= (const CalleeInfo&) = delete;
48
+ auto operator= (CalleeInfo&&) = delete;
49
+ ~CalleeInfo();
50
+ };
51
+
52
+ /**
53
+ * Class which manages running async phase 2, then phase 3
54
+ */
55
+ struct Phase2Runner final : public Runnable {
56
+ std::unique_ptr<ThreePhaseTask> self;
57
+ CalleeInfo info;
58
+ bool did_run = false;
59
+
60
+ Phase2Runner(
61
+ std::unique_ptr<ThreePhaseTask> self,
62
+ CalleeInfo info
63
+ );
64
+ Phase2Runner(const Phase2Runner&) = delete;
65
+ auto operator= (const Phase2Runner&) -> Phase2Runner& = delete;
66
+ ~Phase2Runner() final;
67
+ void Run() final;
68
+ };
69
+
70
+ /**
71
+ * Class which manages running async phase 2 in ignored mode (ie no phase 3)
72
+ */
73
+ struct Phase2RunnerIgnored : public Runnable {
74
+ std::unique_ptr<ThreePhaseTask> self;
75
+ explicit Phase2RunnerIgnored(std::unique_ptr<ThreePhaseTask> self);
76
+ void Run() final;
77
+ };
78
+
79
+ auto RunSync(IsolateHolder& second_isolate, bool allow_async) -> v8::Local<v8::Value>;
80
+
81
+ public:
82
+ ThreePhaseTask() = default;
83
+ ThreePhaseTask(const ThreePhaseTask&) = delete;
84
+ auto operator= (const ThreePhaseTask&) -> ThreePhaseTask& = delete;
85
+ virtual ~ThreePhaseTask() = default;
86
+
87
+ virtual void Phase2() = 0;
88
+ virtual auto Phase2Async(Scheduler::AsyncWait& /*wait*/) -> bool {
89
+ Phase2();
90
+ return false;
91
+ }
92
+
93
+ virtual auto Phase3() -> v8::Local<v8::Value> = 0;
94
+
95
+ template <int async, typename T, typename ...Args>
96
+ static auto Run(IsolateHolder& second_isolate, Args&&... args) -> v8::Local<v8::Value> {
97
+
98
+ if (async == 1) { // Full async, promise returned
99
+ // Build a promise for outer isolate
100
+ v8::Isolate* isolate = v8::Isolate::GetCurrent();
101
+ auto context_local = isolate->GetCurrentContext();
102
+ auto promise_local = Unmaybe(v8::Promise::Resolver::New(context_local));
103
+ auto stack_trace = v8::StackTrace::CurrentStackTrace(isolate, 10);
104
+ FunctorRunners::RunCatchValue([&]() {
105
+ // Schedule Phase2 async
106
+ second_isolate.ScheduleTask(
107
+ std::make_unique<Phase2Runner>(
108
+ std::make_unique<T>(std::forward<Args>(args)...), // <-- Phase1 / ctor called here
109
+ CalleeInfo{promise_local, context_local, stack_trace}
110
+ ), false, true
111
+ );
112
+ }, [&](v8::Local<v8::Value> error) {
113
+ // A C++ error was caught while running ctor (phase 1)
114
+ if (error->IsObject()) {
115
+ StackTraceHolder::AttachStack(error.As<v8::Object>(), stack_trace);
116
+ }
117
+ Unmaybe(promise_local->Reject(context_local, error));
118
+ });
119
+ return promise_local->GetPromise();
120
+ } else if (async == 2) { // Async, promise ignored
121
+ // Schedule Phase2 async
122
+ second_isolate.ScheduleTask(
123
+ std::make_unique<Phase2RunnerIgnored>(
124
+ std::make_unique<T>(std::forward<Args>(args)...) // <-- Phase1 / ctor called here
125
+ ), false, true
126
+ );
127
+ return v8::Undefined(v8::Isolate::GetCurrent());
128
+ } else {
129
+ // Execute synchronously
130
+ T self(std::forward<Args>(args)...);
131
+ return self.RunSync(second_isolate, async == 4);
132
+ }
133
+ }
134
+ };
135
+
136
+ } // namespace ivm
@@ -0,0 +1,15 @@
1
+ #pragma once
2
+ #include <v8.h>
3
+
4
+ namespace ivm {
5
+
6
+ class Transferable {
7
+ public:
8
+ Transferable() = default;
9
+ Transferable(const Transferable&) = delete;
10
+ auto operator= (const Transferable&) = delete;
11
+ virtual ~Transferable() = default;
12
+ virtual auto TransferIn() -> v8::Local<v8::Value> = 0;
13
+ };
14
+
15
+ } // namespace ivm
@@ -0,0 +1,45 @@
1
+ #pragma once
2
+ #include <cassert>
3
+ #include <string>
4
+ #include <functional>
5
+ #include <v8.h>
6
+ #include "generic/error.h"
7
+
8
+ namespace ivm {
9
+
10
+ /**
11
+ * Easy strings
12
+ */
13
+ inline auto v8_string(const char* string) -> v8::Local<v8::String> {
14
+ return Unmaybe(v8::String::NewFromOneByte(v8::Isolate::GetCurrent(), (const uint8_t*)string, v8::NewStringType::kNormal)); // NOLINT
15
+ }
16
+
17
+ inline auto v8_symbol(const char* string) -> v8::Local<v8::String> {
18
+ return Unmaybe(v8::String::NewFromOneByte(v8::Isolate::GetCurrent(), (const uint8_t*)string, v8::NewStringType::kInternalized)); // NOLINT
19
+ }
20
+
21
+ /**
22
+ * Shorthand dereference of Persistent to Local
23
+ */
24
+ template <typename T>
25
+ auto Deref(const v8::Persistent<T>& handle) -> v8::Local<T> {
26
+ return v8::Local<T>::New(v8::Isolate::GetCurrent(), handle);
27
+ }
28
+
29
+ template <typename T>
30
+ auto Deref(const v8::Persistent<T, v8::CopyablePersistentTraits<T>>& handle) -> v8::Local<T> {
31
+ return v8::Local<T>::New(v8::Isolate::GetCurrent(), handle);
32
+ }
33
+
34
+ }
35
+
36
+ #include "remote_handle.h"
37
+
38
+ namespace ivm {
39
+
40
+ template <typename T>
41
+ auto Deref(const RemoteHandle<T>& handle) -> v8::Local<T> {
42
+ return handle.Deref();
43
+ }
44
+
45
+ } // namespace ivm