/src/node/src/api/embed_helpers.cc
Line | Count | Source |
1 | | #include "debug_utils-inl.h" |
2 | | #include "env-inl.h" |
3 | | #include "node.h" |
4 | | #include "node_internals.h" |
5 | | #include "node_snapshot_builder.h" |
6 | | |
7 | | using v8::Context; |
8 | | using v8::Function; |
9 | | using v8::Global; |
10 | | using v8::HandleScope; |
11 | | using v8::Isolate; |
12 | | using v8::Just; |
13 | | using v8::Local; |
14 | | using v8::Locker; |
15 | | using v8::Maybe; |
16 | | using v8::Nothing; |
17 | | using v8::SealHandleScope; |
18 | | using v8::SnapshotCreator; |
19 | | using v8::TryCatch; |
20 | | |
21 | | namespace node { |
22 | | |
23 | 0 | Maybe<ExitCode> SpinEventLoopInternal(Environment* env) { |
24 | 0 | CHECK_NOT_NULL(env); |
25 | 0 | MultiIsolatePlatform* platform = GetMultiIsolatePlatform(env); |
26 | 0 | CHECK_NOT_NULL(platform); |
27 | | |
28 | 0 | Isolate* isolate = env->isolate(); |
29 | 0 | HandleScope handle_scope(isolate); |
30 | 0 | Context::Scope context_scope(env->context()); |
31 | 0 | SealHandleScope seal(isolate); |
32 | |
|
33 | 0 | if (env->is_stopping()) return Nothing<ExitCode>(); |
34 | | |
35 | 0 | env->set_trace_sync_io(env->options()->trace_sync_io); |
36 | 0 | { |
37 | 0 | bool more; |
38 | 0 | env->performance_state()->Mark( |
39 | 0 | node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_START); |
40 | 0 | do { |
41 | 0 | if (env->is_stopping()) break; |
42 | 0 | uv_run(env->event_loop(), UV_RUN_DEFAULT); |
43 | 0 | if (env->is_stopping()) break; |
44 | | |
45 | 0 | platform->DrainTasks(isolate); |
46 | |
|
47 | 0 | more = uv_loop_alive(env->event_loop()); |
48 | 0 | if (more && !env->is_stopping()) continue; |
49 | | |
50 | 0 | if (EmitProcessBeforeExit(env).IsNothing()) |
51 | 0 | break; |
52 | | |
53 | 0 | { |
54 | 0 | HandleScope handle_scope(isolate); |
55 | 0 | if (env->RunSnapshotSerializeCallback().IsEmpty()) { |
56 | 0 | break; |
57 | 0 | } |
58 | 0 | } |
59 | | |
60 | | // Emit `beforeExit` if the loop became alive either after emitting |
61 | | // event, or after running some callbacks. |
62 | 0 | more = uv_loop_alive(env->event_loop()); |
63 | 0 | } while (more == true && !env->is_stopping()); |
64 | 0 | env->performance_state()->Mark( |
65 | 0 | node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_EXIT); |
66 | 0 | } |
67 | 0 | if (env->is_stopping()) return Nothing<ExitCode>(); |
68 | | |
69 | 0 | env->set_trace_sync_io(false); |
70 | | // Clear the serialize callback even though the JS-land queue should |
71 | | // be empty this point so that the deserialized instance won't |
72 | | // attempt to call into JS again. |
73 | 0 | env->set_snapshot_serialize_callback(Local<Function>()); |
74 | |
|
75 | 0 | env->PrintInfoForSnapshotIfDebug(); |
76 | 0 | env->ForEachRealm([](Realm* realm) { realm->VerifyNoStrongBaseObjects(); }); |
77 | 0 | return EmitProcessExitInternal(env); |
78 | 0 | } |
79 | | |
80 | | struct CommonEnvironmentSetup::Impl { |
81 | | MultiIsolatePlatform* platform = nullptr; |
82 | | uv_loop_t loop; |
83 | | std::shared_ptr<ArrayBufferAllocator> allocator; |
84 | | std::optional<SnapshotCreator> snapshot_creator; |
85 | | Isolate* isolate = nullptr; |
86 | | DeleteFnPtr<IsolateData, FreeIsolateData> isolate_data; |
87 | | DeleteFnPtr<Environment, FreeEnvironment> env; |
88 | | Global<Context> main_context; |
89 | | }; |
90 | | |
91 | | CommonEnvironmentSetup::CommonEnvironmentSetup( |
92 | | MultiIsolatePlatform* platform, |
93 | | std::vector<std::string>* errors, |
94 | | const EmbedderSnapshotData* snapshot_data, |
95 | | uint32_t flags, |
96 | | std::function<Environment*(const CommonEnvironmentSetup*)> make_env, |
97 | | const SnapshotConfig* snapshot_config) |
98 | 0 | : impl_(new Impl()) { |
99 | 0 | CHECK_NOT_NULL(platform); |
100 | 0 | CHECK_NOT_NULL(errors); |
101 | | |
102 | 0 | impl_->platform = platform; |
103 | 0 | uv_loop_t* loop = &impl_->loop; |
104 | | // Use `data` to tell the destructor whether the loop was initialized or not. |
105 | 0 | loop->data = nullptr; |
106 | 0 | int ret = uv_loop_init(loop); |
107 | 0 | if (ret != 0) { |
108 | 0 | errors->push_back( |
109 | 0 | SPrintF("Failed to initialize loop: %s", uv_err_name(ret))); |
110 | 0 | return; |
111 | 0 | } |
112 | 0 | loop->data = this; |
113 | |
|
114 | 0 | impl_->allocator = ArrayBufferAllocator::Create(); |
115 | 0 | const std::vector<intptr_t>& external_references = |
116 | 0 | SnapshotBuilder::CollectExternalReferences(); |
117 | 0 | Isolate::CreateParams params; |
118 | 0 | params.array_buffer_allocator = impl_->allocator.get(); |
119 | 0 | params.external_references = external_references.data(); |
120 | 0 | params.cpp_heap = |
121 | 0 | v8::CppHeap::Create(platform, v8::CppHeapCreateParams{{}}).release(); |
122 | |
|
123 | 0 | Isolate* isolate; |
124 | | |
125 | | // Isolates created for snapshotting should be set up differently since |
126 | | // it will be owned by the snapshot creator and needs to be cleaned up |
127 | | // before serialization. |
128 | 0 | if (flags & Flags::kIsForSnapshotting) { |
129 | | // The isolate must be registered before the SnapshotCreator initializes the |
130 | | // isolate, so that the memory reducer can be initialized. |
131 | 0 | isolate = impl_->isolate = Isolate::Allocate(GetOrCreateIsolateGroup()); |
132 | 0 | platform->RegisterIsolate(isolate, loop); |
133 | |
|
134 | 0 | impl_->snapshot_creator.emplace(isolate, params); |
135 | 0 | isolate->SetCaptureStackTraceForUncaughtExceptions( |
136 | 0 | true, |
137 | 0 | static_cast<int>( |
138 | 0 | per_process::cli_options->per_isolate->stack_trace_limit), |
139 | 0 | v8::StackTrace::StackTraceOptions::kDetailed); |
140 | 0 | SetIsolateMiscHandlers(isolate, {}); |
141 | 0 | } else { |
142 | 0 | isolate = impl_->isolate = |
143 | 0 | NewIsolate(¶ms, |
144 | 0 | &impl_->loop, |
145 | 0 | platform, |
146 | 0 | SnapshotData::FromEmbedderWrapper(snapshot_data)); |
147 | 0 | } |
148 | |
|
149 | 0 | { |
150 | 0 | Locker locker(isolate); |
151 | 0 | Isolate::Scope isolate_scope(isolate); |
152 | 0 | HandleScope handle_scope(isolate); |
153 | |
|
154 | 0 | TryCatch bootstrapCatch(isolate); |
155 | 0 | auto print_Exception = OnScopeLeave([&]() { |
156 | 0 | if (bootstrapCatch.HasCaught()) { |
157 | 0 | errors->push_back(FormatCaughtException( |
158 | 0 | isolate, isolate->GetCurrentContext(), bootstrapCatch)); |
159 | 0 | } |
160 | 0 | }); |
161 | |
|
162 | 0 | impl_->isolate_data.reset(CreateIsolateData( |
163 | 0 | isolate, loop, platform, impl_->allocator.get(), snapshot_data)); |
164 | 0 | impl_->isolate_data->set_snapshot_config(snapshot_config); |
165 | |
|
166 | 0 | if (snapshot_data) { |
167 | 0 | impl_->env.reset(make_env(this)); |
168 | 0 | if (impl_->env) { |
169 | 0 | impl_->main_context.Reset(isolate, impl_->env->context()); |
170 | 0 | } |
171 | 0 | return; |
172 | 0 | } |
173 | | |
174 | 0 | Local<Context> context = NewContext(isolate); |
175 | 0 | impl_->main_context.Reset(isolate, context); |
176 | 0 | if (context.IsEmpty()) { |
177 | 0 | errors->push_back("Failed to initialize V8 Context"); |
178 | 0 | return; |
179 | 0 | } |
180 | | |
181 | 0 | Context::Scope context_scope(context); |
182 | 0 | impl_->env.reset(make_env(this)); |
183 | 0 | } |
184 | 0 | } |
185 | | |
186 | | CommonEnvironmentSetup::CommonEnvironmentSetup( |
187 | | MultiIsolatePlatform* platform, |
188 | | std::vector<std::string>* errors, |
189 | | std::function<Environment*(const CommonEnvironmentSetup*)> make_env) |
190 | 0 | : CommonEnvironmentSetup(platform, errors, nullptr, false, make_env) {} |
191 | | |
192 | | std::unique_ptr<CommonEnvironmentSetup> |
193 | | CommonEnvironmentSetup::CreateForSnapshotting( |
194 | | MultiIsolatePlatform* platform, |
195 | | std::vector<std::string>* errors, |
196 | | const std::vector<std::string>& args, |
197 | | const std::vector<std::string>& exec_args, |
198 | 0 | const SnapshotConfig& snapshot_config) { |
199 | | // It's not guaranteed that a context that goes through |
200 | | // v8_inspector::V8Inspector::contextCreated() is runtime-independent, |
201 | | // so do not start the inspector on the main context when building |
202 | | // the default snapshot. |
203 | 0 | uint64_t env_flags = |
204 | 0 | EnvironmentFlags::kDefaultFlags | EnvironmentFlags::kNoCreateInspector; |
205 | |
|
206 | 0 | auto ret = std::unique_ptr<CommonEnvironmentSetup>(new CommonEnvironmentSetup( |
207 | 0 | platform, |
208 | 0 | errors, |
209 | 0 | nullptr, |
210 | 0 | true, |
211 | 0 | [&](const CommonEnvironmentSetup* setup) -> Environment* { |
212 | 0 | return CreateEnvironment( |
213 | 0 | setup->isolate_data(), |
214 | 0 | setup->context(), |
215 | 0 | args, |
216 | 0 | exec_args, |
217 | 0 | static_cast<EnvironmentFlags::Flags>(env_flags)); |
218 | 0 | }, |
219 | 0 | &snapshot_config)); |
220 | 0 | if (!errors->empty()) ret.reset(); |
221 | 0 | return ret; |
222 | 0 | } |
223 | | |
224 | 0 | CommonEnvironmentSetup::~CommonEnvironmentSetup() { |
225 | 0 | if (impl_->isolate != nullptr) { |
226 | 0 | Isolate* isolate = impl_->isolate; |
227 | 0 | { |
228 | 0 | Locker locker(isolate); |
229 | 0 | Isolate::Scope isolate_scope(isolate); |
230 | |
|
231 | 0 | impl_->main_context.Reset(); |
232 | 0 | impl_->env.reset(); |
233 | 0 | impl_->isolate_data.reset(); |
234 | 0 | } |
235 | |
|
236 | 0 | bool platform_finished = false; |
237 | 0 | impl_->platform->AddIsolateFinishedCallback( |
238 | 0 | isolate, |
239 | 0 | [](void* data) { |
240 | 0 | bool* ptr = static_cast<bool*>(data); |
241 | 0 | *ptr = true; |
242 | 0 | }, |
243 | 0 | &platform_finished); |
244 | 0 | if (impl_->snapshot_creator.has_value()) { |
245 | 0 | impl_->snapshot_creator.reset(); |
246 | 0 | } |
247 | 0 | impl_->platform->DisposeIsolate(isolate); |
248 | | |
249 | | // Wait until the platform has cleaned up all relevant resources. |
250 | 0 | while (!platform_finished) |
251 | 0 | uv_run(&impl_->loop, UV_RUN_ONCE); |
252 | 0 | } |
253 | |
|
254 | 0 | if (impl_->isolate || impl_->loop.data != nullptr) |
255 | 0 | CheckedUvLoopClose(&impl_->loop); |
256 | |
|
257 | 0 | delete impl_; |
258 | 0 | } |
259 | | |
260 | 0 | EmbedderSnapshotData::Pointer CommonEnvironmentSetup::CreateSnapshot() { |
261 | 0 | CHECK_NOT_NULL(snapshot_creator()); |
262 | 0 | SnapshotData* snapshot_data = new SnapshotData(); |
263 | 0 | EmbedderSnapshotData::Pointer result{ |
264 | 0 | new EmbedderSnapshotData(snapshot_data, true)}; |
265 | |
|
266 | 0 | auto exit_code = SnapshotBuilder::CreateSnapshot(snapshot_data, this); |
267 | 0 | if (exit_code != ExitCode::kNoFailure) return {}; |
268 | | |
269 | 0 | return result; |
270 | 0 | } |
271 | | |
272 | 0 | Maybe<int> SpinEventLoop(Environment* env) { |
273 | 0 | Maybe<ExitCode> result = SpinEventLoopInternal(env); |
274 | 0 | if (result.IsNothing()) { |
275 | 0 | return Nothing<int>(); |
276 | 0 | } |
277 | 0 | return Just(static_cast<int>(result.FromJust())); |
278 | 0 | } |
279 | | |
280 | 0 | uv_loop_t* CommonEnvironmentSetup::event_loop() const { |
281 | 0 | return &impl_->loop; |
282 | 0 | } |
283 | | |
284 | | std::shared_ptr<ArrayBufferAllocator> |
285 | 0 | CommonEnvironmentSetup::array_buffer_allocator() const { |
286 | 0 | return impl_->allocator; |
287 | 0 | } |
288 | | |
289 | 0 | Isolate* CommonEnvironmentSetup::isolate() const { |
290 | 0 | return impl_->isolate; |
291 | 0 | } |
292 | | |
293 | 0 | IsolateData* CommonEnvironmentSetup::isolate_data() const { |
294 | 0 | return impl_->isolate_data.get(); |
295 | 0 | } |
296 | | |
297 | 0 | Environment* CommonEnvironmentSetup::env() const { |
298 | 0 | return impl_->env.get(); |
299 | 0 | } |
300 | | |
301 | 0 | v8::Local<v8::Context> CommonEnvironmentSetup::context() const { |
302 | 0 | return impl_->main_context.Get(impl_->isolate); |
303 | 0 | } |
304 | | |
305 | 0 | v8::SnapshotCreator* CommonEnvironmentSetup::snapshot_creator() { |
306 | 0 | return impl_->snapshot_creator ? &impl_->snapshot_creator.value() : nullptr; |
307 | 0 | } |
308 | | |
309 | | void EmbedderSnapshotData::DeleteSnapshotData::operator()( |
310 | 0 | const EmbedderSnapshotData* data) const { |
311 | 0 | CHECK_IMPLIES(data->owns_impl_, data->impl_); |
312 | 0 | if (data->owns_impl_ && |
313 | 0 | data->impl_->data_ownership == SnapshotData::DataOwnership::kOwned) { |
314 | 0 | delete data->impl_; |
315 | 0 | } |
316 | 0 | delete data; |
317 | 0 | } |
318 | | |
319 | 0 | EmbedderSnapshotData::Pointer EmbedderSnapshotData::BuiltinSnapshotData() { |
320 | 0 | return EmbedderSnapshotData::Pointer{new EmbedderSnapshotData( |
321 | 0 | SnapshotBuilder::GetEmbeddedSnapshotData(), false)}; |
322 | 0 | } |
323 | | |
324 | | EmbedderSnapshotData::Pointer EmbedderSnapshotData::FromBlob( |
325 | 0 | const std::vector<char>& in) { |
326 | 0 | return FromBlob(std::string_view(in.data(), in.size())); |
327 | 0 | } |
328 | | |
329 | | EmbedderSnapshotData::Pointer EmbedderSnapshotData::FromBlob( |
330 | 0 | std::string_view in) { |
331 | 0 | SnapshotData* snapshot_data = new SnapshotData(); |
332 | 0 | CHECK_EQ(snapshot_data->data_ownership, SnapshotData::DataOwnership::kOwned); |
333 | 0 | EmbedderSnapshotData::Pointer result{ |
334 | 0 | new EmbedderSnapshotData(snapshot_data, true)}; |
335 | 0 | if (!SnapshotData::FromBlob(snapshot_data, in)) { |
336 | 0 | return {}; |
337 | 0 | } |
338 | 0 | return result; |
339 | 0 | } |
340 | | |
341 | 0 | EmbedderSnapshotData::Pointer EmbedderSnapshotData::FromFile(FILE* in) { |
342 | 0 | return FromBlob(ReadFileSync(in)); |
343 | 0 | } |
344 | | |
345 | 0 | std::vector<char> EmbedderSnapshotData::ToBlob() const { |
346 | 0 | return impl_->ToBlob(); |
347 | 0 | } |
348 | | |
349 | 0 | void EmbedderSnapshotData::ToFile(FILE* out) const { |
350 | 0 | impl_->ToFile(out); |
351 | 0 | } |
352 | | |
353 | | EmbedderSnapshotData::EmbedderSnapshotData(const SnapshotData* impl, |
354 | | bool owns_impl) |
355 | 0 | : impl_(impl), owns_impl_(owns_impl) {} |
356 | | |
357 | 0 | bool EmbedderSnapshotData::CanUseCustomSnapshotPerIsolate() { |
358 | 0 | return false; |
359 | 0 | } |
360 | | |
361 | | } // namespace node |