mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 18:25:13 +08:00
At the point of `FreeEnvironment` and onwards, no JavaScript execution associated with the Environment should be triggered. Avoid draining platform tasks that can trigger JavaScript execution in `FreeEnvironment`. The holder of `node::Environment` should immediately call `node::MultiIsolatePlatform::UnregisterIsolate` and `v8::Isolate::Dispose` to cancel pending foreground tasks and join concurrent tasks after the environment was freed. `NodePlatform` can properly handle the case in `RunForegroundTask` when an Isolate out-lives its associated `node::Environment`. PR-URL: https://github.com/nodejs/node/pull/51290 Fixes: https://github.com/nodejs/node/issues/47748 Fixes: https://github.com/nodejs/node/issues/49344 Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
167 lines
5.1 KiB
C++
167 lines
5.1 KiB
C++
#include "node_main_instance.h"
|
|
#include <memory>
|
|
#if HAVE_OPENSSL
|
|
#include "crypto/crypto_util.h"
|
|
#endif // HAVE_OPENSSL
|
|
#include "debug_utils-inl.h"
|
|
#include "node_builtins.h"
|
|
#include "node_external_reference.h"
|
|
#include "node_internals.h"
|
|
#include "node_options-inl.h"
|
|
#include "node_realm.h"
|
|
#include "node_sea.h"
|
|
#include "node_snapshot_builder.h"
|
|
#include "node_snapshotable.h"
|
|
#include "node_v8_platform-inl.h"
|
|
#include "util-inl.h"
|
|
#if defined(LEAK_SANITIZER)
|
|
#include <sanitizer/lsan_interface.h>
|
|
#endif
|
|
|
|
#if HAVE_INSPECTOR
|
|
#include "inspector/worker_inspector.h" // ParentInspectorHandle
|
|
#endif
|
|
|
|
namespace node {
|
|
|
|
using v8::Context;
|
|
using v8::HandleScope;
|
|
using v8::Isolate;
|
|
using v8::Local;
|
|
using v8::Locker;
|
|
|
|
NodeMainInstance::NodeMainInstance(const SnapshotData* snapshot_data,
|
|
uv_loop_t* event_loop,
|
|
MultiIsolatePlatform* platform,
|
|
const std::vector<std::string>& args,
|
|
const std::vector<std::string>& exec_args)
|
|
: args_(args),
|
|
exec_args_(exec_args),
|
|
array_buffer_allocator_(ArrayBufferAllocator::Create()),
|
|
isolate_(nullptr),
|
|
platform_(platform),
|
|
isolate_data_(),
|
|
isolate_params_(std::make_unique<Isolate::CreateParams>()),
|
|
snapshot_data_(snapshot_data) {
|
|
isolate_params_->array_buffer_allocator = array_buffer_allocator_.get();
|
|
|
|
isolate_ =
|
|
NewIsolate(isolate_params_.get(), event_loop, platform, snapshot_data);
|
|
CHECK_NOT_NULL(isolate_);
|
|
|
|
// If the indexes are not nullptr, we are not deserializing
|
|
isolate_data_.reset(
|
|
CreateIsolateData(isolate_,
|
|
event_loop,
|
|
platform,
|
|
array_buffer_allocator_.get(),
|
|
snapshot_data->AsEmbedderWrapper().get()));
|
|
|
|
isolate_data_->max_young_gen_size =
|
|
isolate_params_->constraints.max_young_generation_size_in_bytes();
|
|
}
|
|
|
|
NodeMainInstance::~NodeMainInstance() {
|
|
if (isolate_params_ == nullptr) {
|
|
return;
|
|
}
|
|
|
|
{
|
|
#ifdef DEBUG
|
|
// node::Environment has been disposed and no JavaScript Execution is
|
|
// allowed at this point.
|
|
// Create a scope to check that no JavaScript is executed in debug build
|
|
// and proactively crash the process in the case JavaScript is being
|
|
// executed.
|
|
// Isolate::Dispose() must be invoked outside of this scope to avoid
|
|
// use-after-free.
|
|
Isolate::DisallowJavascriptExecutionScope disallow_js(
|
|
isolate_, Isolate::DisallowJavascriptExecutionScope::CRASH_ON_FAILURE);
|
|
#endif
|
|
// This should only be done on a main instance that owns its isolate.
|
|
// IsolateData must be freed before UnregisterIsolate() is called.
|
|
isolate_data_.reset();
|
|
platform_->UnregisterIsolate(isolate_);
|
|
}
|
|
isolate_->Dispose();
|
|
}
|
|
|
|
ExitCode NodeMainInstance::Run() {
|
|
Locker locker(isolate_);
|
|
Isolate::Scope isolate_scope(isolate_);
|
|
HandleScope handle_scope(isolate_);
|
|
|
|
ExitCode exit_code = ExitCode::kNoFailure;
|
|
DeleteFnPtr<Environment, FreeEnvironment> env =
|
|
CreateMainEnvironment(&exit_code);
|
|
CHECK_NOT_NULL(env);
|
|
|
|
Context::Scope context_scope(env->context());
|
|
Run(&exit_code, env.get());
|
|
return exit_code;
|
|
}
|
|
|
|
void NodeMainInstance::Run(ExitCode* exit_code, Environment* env) {
|
|
if (*exit_code == ExitCode::kNoFailure) {
|
|
bool runs_sea_code = false;
|
|
#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
|
|
if (sea::IsSingleExecutable()) {
|
|
sea::SeaResource sea = sea::FindSingleExecutableResource();
|
|
if (!sea.use_snapshot()) {
|
|
runs_sea_code = true;
|
|
std::string_view code = sea.main_code_or_snapshot;
|
|
LoadEnvironment(env, code);
|
|
}
|
|
}
|
|
#endif
|
|
// Either there is already a snapshot main function from SEA, or it's not
|
|
// a SEA at all.
|
|
if (!runs_sea_code) {
|
|
LoadEnvironment(env, StartExecutionCallback{});
|
|
}
|
|
|
|
*exit_code =
|
|
SpinEventLoopInternal(env).FromMaybe(ExitCode::kGenericUserError);
|
|
}
|
|
|
|
#if defined(LEAK_SANITIZER)
|
|
__lsan_do_leak_check();
|
|
#endif
|
|
}
|
|
|
|
DeleteFnPtr<Environment, FreeEnvironment>
|
|
NodeMainInstance::CreateMainEnvironment(ExitCode* exit_code) {
|
|
*exit_code = ExitCode::kNoFailure; // Reset the exit code to 0
|
|
|
|
HandleScope handle_scope(isolate_);
|
|
|
|
// TODO(addaleax): This should load a real per-Isolate option, currently
|
|
// this is still effectively per-process.
|
|
if (isolate_data_->options()->track_heap_objects) {
|
|
isolate_->GetHeapProfiler()->StartTrackingHeapObjects(true);
|
|
}
|
|
|
|
Local<Context> context;
|
|
DeleteFnPtr<Environment, FreeEnvironment> env;
|
|
|
|
if (snapshot_data_ != nullptr) {
|
|
env.reset(CreateEnvironment(isolate_data_.get(),
|
|
Local<Context>(), // read from snapshot
|
|
args_,
|
|
exec_args_));
|
|
#if HAVE_OPENSSL
|
|
crypto::InitCryptoOnce(isolate_);
|
|
#endif // HAVE_OPENSSL
|
|
} else {
|
|
context = NewContext(isolate_);
|
|
CHECK(!context.IsEmpty());
|
|
Context::Scope context_scope(context);
|
|
env.reset(
|
|
CreateEnvironment(isolate_data_.get(), context, args_, exec_args_));
|
|
}
|
|
|
|
return env;
|
|
}
|
|
|
|
} // namespace node
|