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