diff --git a/src/aliased_buffer.h b/src/aliased_buffer.h index e762e8ede8ebee..408f158b7880ca 100644 --- a/src/aliased_buffer.h +++ b/src/aliased_buffer.h @@ -4,11 +4,14 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include +#include #include "util-inl.h" #include "v8.h" namespace node { +typedef size_t AliasedBufferInfo; + /** * Do not use this class directly when creating instances of it - use the * Aliased*Array defined at the end of this file instead. @@ -32,9 +35,15 @@ template ::value>> class AliasedBufferBase { public: - AliasedBufferBase(v8::Isolate* isolate, const size_t count) - : isolate_(isolate), count_(count), byte_offset_(0) { + AliasedBufferBase(v8::Isolate* isolate, + const size_t count, + const AliasedBufferInfo* info = nullptr) + : isolate_(isolate), count_(count), byte_offset_(0), info_(info) { CHECK_GT(count, 0); + if (info != nullptr) { + // Will be deserialized later. + return; + } const v8::HandleScope handle_scope(isolate_); const size_t size_in_bytes = MultiplyWithOverflowCheck(sizeof(NativeT), count); @@ -62,10 +71,17 @@ class AliasedBufferBase { v8::Isolate* isolate, const size_t byte_offset, const size_t count, - const AliasedBufferBase& backing_buffer) - : isolate_(isolate), count_(count), byte_offset_(byte_offset) { + const AliasedBufferBase& backing_buffer, + const AliasedBufferInfo* info = nullptr) + : isolate_(isolate), + count_(count), + byte_offset_(byte_offset), + info_(info) { + if (info != nullptr) { + // Will be deserialized later. + return; + } const v8::HandleScope handle_scope(isolate_); - v8::Local ab = backing_buffer.GetArrayBuffer(); // validate that the byte_offset is aligned with sizeof(NativeT) @@ -86,10 +102,33 @@ class AliasedBufferBase { count_(that.count_), byte_offset_(that.byte_offset_), buffer_(that.buffer_) { + DCHECK_NULL(info_); js_array_ = v8::Global(that.isolate_, that.GetJSArray()); } + AliasedBufferInfo Serialize(v8::Local context, + v8::SnapshotCreator* creator) { + DCHECK_NULL(info_); + return creator->AddData(context, GetJSArray()); + } + + inline void Deserialize(v8::Local context) { + DCHECK_NOT_NULL(info_); + v8::Local arr = + context->GetDataFromSnapshotOnce(*info_).ToLocalChecked(); + // These may not hold true for AliasedBuffers that have grown, so should + // be removed when we expand the snapshot support. + DCHECK_EQ(count_, arr->Length()); + DCHECK_EQ(byte_offset_, arr->ByteOffset()); + uint8_t* raw = + static_cast(arr->Buffer()->GetBackingStore()->Data()); + buffer_ = reinterpret_cast(raw + byte_offset_); + js_array_.Reset(isolate_, arr); + info_ = nullptr; + } + AliasedBufferBase& operator=(AliasedBufferBase&& that) noexcept { + DCHECK_NULL(info_); this->~AliasedBufferBase(); isolate_ = that.isolate_; count_ = that.count_; @@ -155,6 +194,7 @@ class AliasedBufferBase { * Get the underlying v8 TypedArray overlayed on top of the native buffer */ v8::Local GetJSArray() const { + DCHECK_NULL(info_); return js_array_.Get(isolate_); } @@ -171,6 +211,7 @@ class AliasedBufferBase { * through the GetValue/SetValue/operator[] methods */ inline const NativeT* GetNativeBuffer() const { + DCHECK_NULL(info_); return buffer_; } @@ -186,6 +227,7 @@ class AliasedBufferBase { */ inline void SetValue(const size_t index, NativeT value) { DCHECK_LT(index, count_); + DCHECK_NULL(info_); buffer_[index] = value; } @@ -193,6 +235,7 @@ class AliasedBufferBase { * Get value at position index */ inline const NativeT GetValue(const size_t index) const { + DCHECK_NULL(info_); DCHECK_LT(index, count_); return buffer_[index]; } @@ -201,6 +244,7 @@ class AliasedBufferBase { * Effectively, a synonym for GetValue/SetValue */ Reference operator[](size_t index) { + DCHECK_NULL(info_); return Reference(this, index); } @@ -216,6 +260,7 @@ class AliasedBufferBase { // Should only be used on an owning array, not one created as a sub array of // an owning `AliasedBufferBase`. void reserve(size_t new_capacity) { + DCHECK_NULL(info_); DCHECK_GE(new_capacity, count_); DCHECK_EQ(byte_offset_, 0); const v8::HandleScope handle_scope(isolate_); @@ -244,11 +289,14 @@ class AliasedBufferBase { } private: - v8::Isolate* isolate_; - size_t count_; - size_t byte_offset_; - NativeT* buffer_; + v8::Isolate* isolate_ = nullptr; + size_t count_ = 0; + size_t byte_offset_ = 0; + NativeT* buffer_ = nullptr; v8::Global js_array_; + + // Deserialize data + const AliasedBufferInfo* info_ = nullptr; }; typedef AliasedBufferBase AliasedInt32Array; diff --git a/src/api/environment.cc b/src/api/environment.cc index 1104d758f63e36..b288ee7646926d 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -350,13 +350,7 @@ Environment* CreateEnvironment( // TODO(addaleax): This is a much better place for parsing per-Environment // options than the global parse call. Environment* env = new Environment( - isolate_data, - context, - args, - exec_args, - flags, - thread_id); - env->InitializeMainContext(context); + isolate_data, context, args, exec_args, nullptr, flags, thread_id); #if HAVE_INSPECTOR if (inspector_parent_handle) { env->InitializeInspector( diff --git a/src/debug_utils.h b/src/debug_utils.h index 845a78c53c30fc..4915fbae325ecc 100644 --- a/src/debug_utils.h +++ b/src/debug_utils.h @@ -46,7 +46,8 @@ void FWrite(FILE* file, const std::string& str); V(INSPECTOR_PROFILER) \ V(CODE_CACHE) \ V(NGTCP2_DEBUG) \ - V(WASI) + V(WASI) \ + V(MKSNAPSHOT) enum class DebugCategory { #define V(name) name, diff --git a/src/env-inl.h b/src/env-inl.h index 20794a3cbe9539..44fa1125a3c455 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -70,29 +70,6 @@ inline v8::Local IsolateData::async_wrap_provider(int index) const { return async_wrap_providers_[index].Get(isolate_); } -inline AsyncHooks::AsyncHooks() - : async_ids_stack_(env()->isolate(), 16 * 2), - fields_(env()->isolate(), kFieldsCount), - async_id_fields_(env()->isolate(), kUidFieldsCount) { - clear_async_id_stack(); - - // Always perform async_hooks checks, not just when async_hooks is enabled. - // TODO(AndreasMadsen): Consider removing this for LTS releases. - // See discussion in https://github.com/nodejs/node/pull/15454 - // When removing this, do it by reverting the commit. Otherwise the test - // and flag changes won't be included. - fields_[kCheck] = 1; - - // kDefaultTriggerAsyncId should be -1, this indicates that there is no - // specified default value and it should fallback to the executionAsyncId. - // 0 is not used as the magic value, because that indicates a missing context - // which is different from a default context. - async_id_fields_[AsyncHooks::kDefaultTriggerAsyncId] = -1; - - // kAsyncIdCounter should start at 1 because that'll be the id the execution - // context during bootstrap (code that runs before entering uv_run()). - async_id_fields_[AsyncHooks::kAsyncIdCounter] = 1; -} inline AliasedUint32Array& AsyncHooks::fields() { return fields_; } @@ -277,9 +254,6 @@ inline void Environment::PopAsyncCallbackScope() { async_callback_scope_depth_--; } -inline ImmediateInfo::ImmediateInfo(v8::Isolate* isolate) - : fields_(isolate, kFieldsCount) {} - inline AliasedUint32Array& ImmediateInfo::fields() { return fields_; } @@ -304,9 +278,6 @@ inline void ImmediateInfo::ref_count_dec(uint32_t decrement) { fields_[kRefCount] -= decrement; } -inline TickInfo::TickInfo(v8::Isolate* isolate) - : fields_(isolate, kFieldsCount) {} - inline AliasedUint8Array& TickInfo::fields() { return fields_; } diff --git a/src/env.cc b/src/env.cc index 494c1877f4483b..e30e7d6e1dae6a 100644 --- a/src/env.cc +++ b/src/env.cc @@ -21,7 +21,9 @@ #include #include +#include #include +#include #include namespace node { @@ -309,18 +311,26 @@ Environment::Environment(IsolateData* isolate_data, Isolate* isolate, const std::vector& args, const std::vector& exec_args, + const EnvSerializeInfo* env_info, EnvironmentFlags::Flags flags, ThreadId thread_id) : isolate_(isolate), isolate_data_(isolate_data), - immediate_info_(isolate), - tick_info_(isolate), + async_hooks_(isolate, MAYBE_FIELD_PTR(env_info, async_hooks)), + immediate_info_(isolate, MAYBE_FIELD_PTR(env_info, immediate_info)), + tick_info_(isolate, MAYBE_FIELD_PTR(env_info, tick_info)), timer_base_(uv_now(isolate_data->event_loop())), exec_argv_(exec_args), argv_(args), exec_path_(GetExecPath(args)), - should_abort_on_uncaught_toggle_(isolate_, 1), - stream_base_state_(isolate_, StreamBase::kNumStreamBaseStateFields), + should_abort_on_uncaught_toggle_( + isolate_, + 1, + MAYBE_FIELD_PTR(env_info, should_abort_on_uncaught_toggle)), + stream_base_state_(isolate_, + StreamBase::kNumStreamBaseStateFields, + MAYBE_FIELD_PTR(env_info, stream_base_state)), + environment_start_time_(PERFORMANCE_NOW()), flags_(flags), thread_id_(thread_id.id == static_cast(-1) ? AllocateEnvironmentThreadId().id @@ -370,14 +380,8 @@ Environment::Environment(IsolateData* isolate_data, destroy_async_id_list_.reserve(512); - performance_state_ = std::make_unique(isolate); - performance_state_->Mark( - performance::NODE_PERFORMANCE_MILESTONE_ENVIRONMENT); - performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_NODE_START, - per_process::node_start_time); - performance_state_->Mark( - performance::NODE_PERFORMANCE_MILESTONE_V8_START, - performance::performance_v8_start); + performance_state_ = std::make_unique( + isolate, MAYBE_FIELD_PTR(env_info, performance_state)); if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( TRACING_CATEGORY_NODE1(environment)) != 0) { @@ -395,13 +399,6 @@ Environment::Environment(IsolateData* isolate_data, std::move(traced_value)); } - // By default, always abort when --abort-on-uncaught-exception was passed. - should_abort_on_uncaught_toggle_[0] = 1; - - if (options_->no_force_async_hooks_checks) { - async_hooks_.no_force_checks(); - } - // This adjusts the return value of base_object_count() so that tests that // check the count do not have to account for internally created BaseObjects. initial_base_object_count_ = base_object_count(); @@ -411,23 +408,42 @@ Environment::Environment(IsolateData* isolate_data, Local context, const std::vector& args, const std::vector& exec_args, + const EnvSerializeInfo* env_info, EnvironmentFlags::Flags flags, ThreadId thread_id) : Environment(isolate_data, context->GetIsolate(), args, exec_args, + env_info, flags, thread_id) { - InitializeMainContext(context); + InitializeMainContext(context, env_info); } -void Environment::InitializeMainContext(Local context) { +void Environment::InitializeMainContext(Local context, + const EnvSerializeInfo* env_info) { context_.Reset(context->GetIsolate(), context); AssignToContext(context, ContextInfo("")); - // TODO(joyeecheung): deserialize when the snapshot covers the environment - // properties. - CreateProperties(); + if (env_info != nullptr) { + DeserializeProperties(env_info); + } else { + CreateProperties(); + } + + if (options_->no_force_async_hooks_checks) { + async_hooks_.no_force_checks(); + } + + // By default, always abort when --abort-on-uncaught-exception was passed. + should_abort_on_uncaught_toggle_[0] = 1; + + performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_ENVIRONMENT, + environment_start_time_); + performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_NODE_START, + per_process::node_start_time); + performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_V8_START, + performance::performance_v8_start); // This adjusts the return value of base_object_count() so that tests that // check the count do not have to account for internally created BaseObjects. @@ -474,8 +490,8 @@ Environment::~Environment() { inspector_agent_.reset(); #endif - context()->SetAlignedPointerInEmbedderData( - ContextEmbedderIndex::kEnvironment, nullptr); + context()->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kEnvironment, + nullptr); if (trace_state_observer_) { tracing::AgentWriterHandle* writer = GetTracingAgentWriter(); @@ -951,14 +967,154 @@ void Environment::CollectUVExceptionInfo(Local object, syscall, message, path, dest); } +ImmediateInfo::ImmediateInfo(v8::Isolate* isolate, const SerializeInfo* info) + : fields_(isolate, kFieldsCount, MAYBE_FIELD_PTR(info, fields)) {} + +ImmediateInfo::SerializeInfo ImmediateInfo::Serialize( + v8::Local context, v8::SnapshotCreator* creator) { + return {fields_.Serialize(context, creator)}; +} + +void ImmediateInfo::Deserialize(Local context) { + fields_.Deserialize(context); +} + +std::ostream& operator<<(std::ostream& output, + const ImmediateInfo::SerializeInfo& i) { + output << "{ " << i.fields << " }"; + return output; +} + void ImmediateInfo::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("fields", fields_); } +TickInfo::SerializeInfo TickInfo::Serialize(v8::Local context, + v8::SnapshotCreator* creator) { + return {fields_.Serialize(context, creator)}; +} + +void TickInfo::Deserialize(Local context) { + fields_.Deserialize(context); +} + +std::ostream& operator<<(std::ostream& output, + const TickInfo::SerializeInfo& i) { + output << "{ " << i.fields << " }"; + return output; +} + void TickInfo::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("fields", fields_); } +TickInfo::TickInfo(v8::Isolate* isolate, const SerializeInfo* info) + : fields_( + isolate, kFieldsCount, info == nullptr ? nullptr : &(info->fields)) {} + +AsyncHooks::AsyncHooks(v8::Isolate* isolate, const SerializeInfo* info) + : async_ids_stack_(isolate, 16 * 2, MAYBE_FIELD_PTR(info, async_ids_stack)), + fields_(isolate, kFieldsCount, MAYBE_FIELD_PTR(info, fields)), + async_id_fields_( + isolate, kUidFieldsCount, MAYBE_FIELD_PTR(info, async_id_fields)), + info_(info) { + v8::HandleScope handle_scope(isolate); + if (info == nullptr) { + clear_async_id_stack(); + + // Always perform async_hooks checks, not just when async_hooks is enabled. + // TODO(AndreasMadsen): Consider removing this for LTS releases. + // See discussion in https://github.com/nodejs/node/pull/15454 + // When removing this, do it by reverting the commit. Otherwise the test + // and flag changes won't be included. + fields_[kCheck] = 1; + + // kDefaultTriggerAsyncId should be -1, this indicates that there is no + // specified default value and it should fallback to the executionAsyncId. + // 0 is not used as the magic value, because that indicates a missing + // context which is different from a default context. + async_id_fields_[AsyncHooks::kDefaultTriggerAsyncId] = -1; + + // kAsyncIdCounter should start at 1 because that'll be the id the execution + // context during bootstrap (code that runs before entering uv_run()). + async_id_fields_[AsyncHooks::kAsyncIdCounter] = 1; + } +} + +void AsyncHooks::Deserialize(Local context) { + async_ids_stack_.Deserialize(context); + fields_.Deserialize(context); + async_id_fields_.Deserialize(context); + if (info_->js_execution_async_resources != 0) { + v8::Local arr = context + ->GetDataFromSnapshotOnce( + info_->js_execution_async_resources) + .ToLocalChecked(); + js_execution_async_resources_.Reset(context->GetIsolate(), arr); + } + + native_execution_async_resources_.resize( + info_->native_execution_async_resources.size()); + for (size_t i = 0; i < info_->native_execution_async_resources.size(); ++i) { + v8::Local obj = + context + ->GetDataFromSnapshotOnce( + info_->native_execution_async_resources[i]) + .ToLocalChecked(); + native_execution_async_resources_[i].Reset(context->GetIsolate(), obj); + } + info_ = nullptr; +} + +std::ostream& operator<<(std::ostream& output, + const std::vector& v) { + output << "{ "; + for (const SnapshotIndex i : v) { + output << i << ", "; + } + output << " }"; + return output; +} + +std::ostream& operator<<(std::ostream& output, + const AsyncHooks::SerializeInfo& i) { + output << "{\n" + << " " << i.async_ids_stack << ", // async_ids_stack\n" + << " " << i.fields << ", // fields\n" + << " " << i.async_id_fields << ", // async_id_fields\n" + << " " << i.js_execution_async_resources + << ", // js_execution_async_resources\n" + << " " << i.native_execution_async_resources + << ", // native_execution_async_resources\n" + << "}"; + return output; +} + +AsyncHooks::SerializeInfo AsyncHooks::Serialize(Local context, + SnapshotCreator* creator) { + SerializeInfo info; + info.async_ids_stack = async_ids_stack_.Serialize(context, creator); + info.fields = fields_.Serialize(context, creator); + info.async_id_fields = async_id_fields_.Serialize(context, creator); + if (!js_execution_async_resources_.IsEmpty()) { + info.js_execution_async_resources = creator->AddData( + context, js_execution_async_resources_.Get(context->GetIsolate())); + CHECK_NE(info.js_execution_async_resources, 0); + } else { + info.js_execution_async_resources = 0; + } + + info.native_execution_async_resources.resize( + native_execution_async_resources_.size()); + for (size_t i = 0; i < native_execution_async_resources_.size(); i++) { + info.native_execution_async_resources[i] = creator->AddData( + context, + native_execution_async_resources_[i].Get(context->GetIsolate())); + } + + return info; +} + void AsyncHooks::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("async_ids_stack", async_ids_stack_); tracker->TrackField("fields", fields_); @@ -1030,6 +1186,174 @@ void Environment::RemoveUnmanagedFd(int fd) { } } +void Environment::ForEachBaseObject(BaseObjectIterator iterator) { + size_t i = 0; + for (const auto& hook : cleanup_hooks_) { + BaseObject* obj = hook.GetBaseObject(); + if (obj != nullptr) iterator(i, obj); + i++; + } +} + +void PrintBaseObject(size_t i, BaseObject* obj) { + std::cout << "#" << i << " " << obj << ": " << obj->MemoryInfoName() << "\n"; +} + +void Environment::PrintAllBaseObjects() { + std::cout << "BaseObjects\n"; + ForEachBaseObject(PrintBaseObject); +} + +EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) { + EnvSerializeInfo info; + Local ctx = context(); + + info.async_hooks = async_hooks_.Serialize(ctx, creator); + info.immediate_info = immediate_info_.Serialize(ctx, creator); + info.tick_info = tick_info_.Serialize(ctx, creator); + info.performance_state = performance_state_->Serialize(ctx, creator); + info.stream_base_state = stream_base_state_.Serialize(ctx, creator); + info.should_abort_on_uncaught_toggle = + should_abort_on_uncaught_toggle_.Serialize(ctx, creator); + + size_t id = 0; +#define V(PropertyName, TypeName) \ + do { \ + Local field = PropertyName(); \ + if (!field.IsEmpty()) { \ + size_t index = creator->AddData(field); \ + info.persistent_templates.push_back({#PropertyName, id, index}); \ + } \ + id++; \ + } while (0); + ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES(V) +#undef V + + id = 0; +#define V(PropertyName, TypeName) \ + do { \ + Local field = PropertyName(); \ + if (!field.IsEmpty()) { \ + size_t index = creator->AddData(ctx, field); \ + info.persistent_values.push_back({#PropertyName, id, index}); \ + } \ + id++; \ + } while (0); + ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) +#undef V + + info.context = creator->AddData(ctx, context()); + return info; +} + +std::ostream& operator<<(std::ostream& output, + const std::vector& vec) { + output << "{\n"; + for (const auto& info : vec) { + output << " { \"" << info.name << "\", " << std::to_string(info.id) << ", " + << std::to_string(info.index) << " },\n"; + } + output << "}"; + return output; +} + +std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) { + output << "{\n" + << "// -- async_hooks begins --\n" + << i.async_hooks << ",\n" + << "// -- async_hooks begins --\n" + << i.tick_info << ", // tick_info\n" + << i.immediate_info << ", // immediate_info\n" + << "// -- performance_state begins --\n" + << i.performance_state << ",\n" + << "// -- performance_state ends --\n" + << i.stream_base_state << ", // stream_base_state\n" + << i.should_abort_on_uncaught_toggle + << ", // should_abort_on_uncaught_toggle\n" + << "// -- persistent_templates begins --\n" + << i.persistent_templates << ",\n" + << "// persistent_templates ends --\n" + << "// -- persistent_values begins --\n" + << i.persistent_values << ",\n" + << "// -- persistent_values ends --\n" + << i.context << ", // context\n" + << "}"; + return output; +} + +void Environment::DeserializeProperties(const EnvSerializeInfo* info) { + Local ctx = context(); + + async_hooks_.Deserialize(ctx); + immediate_info_.Deserialize(ctx); + tick_info_.Deserialize(ctx); + performance_state_->Deserialize(ctx); + stream_base_state_.Deserialize(ctx); + should_abort_on_uncaught_toggle_.Deserialize(ctx); + + if (enabled_debug_list_.enabled(DebugCategory::MKSNAPSHOT)) { + fprintf(stderr, "deserializing...\n"); + std::cerr << *info << "\n"; + } + + const std::vector& templates = info->persistent_templates; + size_t i = 0; // index to the array + size_t id = 0; +#define V(PropertyName, TypeName) \ + do { \ + if (templates.size() > i && id == templates[i].id) { \ + const PropInfo& d = templates[i]; \ + DCHECK_EQ(d.name, #PropertyName); \ + MaybeLocal maybe_field = \ + isolate_->GetDataFromSnapshotOnce(d.index); \ + Local field; \ + if (!maybe_field.ToLocal(&field)) { \ + fprintf(stderr, \ + "Failed to deserialize environment template " #PropertyName \ + "\n"); \ + } \ + set_##PropertyName(field); \ + i++; \ + } \ + } while (0); \ + id++; + ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES(V); +#undef V + + i = 0; // index to the array + id = 0; + const std::vector& values = info->persistent_values; +#define V(PropertyName, TypeName) \ + do { \ + if (values.size() > i && id == values[i].id) { \ + const PropInfo& d = values[i]; \ + DCHECK_EQ(d.name, #PropertyName); \ + MaybeLocal maybe_field = \ + ctx->GetDataFromSnapshotOnce(d.index); \ + Local field; \ + if (!maybe_field.ToLocal(&field)) { \ + fprintf(stderr, \ + "Failed to deserialize environment value " #PropertyName \ + "\n"); \ + } \ + set_##PropertyName(field); \ + i++; \ + } \ + } while (0); \ + id++; + ENVIRONMENT_STRONG_PERSISTENT_VALUES(V); +#undef V + + MaybeLocal maybe_ctx_from_snapshot = + ctx->GetDataFromSnapshotOnce(info->context); + Local ctx_from_snapshot; + if (!maybe_ctx_from_snapshot.ToLocal(&ctx_from_snapshot)) { + fprintf(stderr, + "Failed to deserialize context back reference from the snapshot\n"); + } + CHECK_EQ(ctx_from_snapshot, ctx); +} + void Environment::BuildEmbedderGraph(Isolate* isolate, EmbedderGraph* graph, void* data) { diff --git a/src/env.h b/src/env.h index 8c1bf71ddfaee4..c6f6ac481544f5 100644 --- a/src/env.h +++ b/src/env.h @@ -36,6 +36,7 @@ #include "node_binding.h" #include "node_main_instance.h" #include "node_options.h" +#include "node_perf_common.h" #include "req_wrap.h" #include "util.h" #include "uv.h" @@ -45,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -532,6 +534,7 @@ constexpr size_t kFsStatsBufferLength = class Environment; struct AllocatedBuffer; +typedef size_t SnapshotIndex; class IsolateData : public MemoryRetainer { public: IsolateData(v8::Isolate* isolate, @@ -645,6 +648,8 @@ namespace per_process { extern std::shared_ptr system_environment; } +struct EnvSerializeInfo; + class AsyncHooks : public MemoryRetainer { public: SET_MEMORY_INFO_NAME(AsyncHooks) @@ -722,9 +727,22 @@ class AsyncHooks : public MemoryRetainer { double old_default_trigger_async_id_; }; + struct SerializeInfo { + AliasedBufferInfo async_ids_stack; + AliasedBufferInfo fields; + AliasedBufferInfo async_id_fields; + SnapshotIndex js_execution_async_resources; + std::vector native_execution_async_resources; + }; + + SerializeInfo Serialize(v8::Local context, + v8::SnapshotCreator* creator); + void Deserialize(v8::Local context); + private: friend class Environment; // So we can call the constructor. - inline AsyncHooks(); + explicit AsyncHooks(v8::Isolate* isolate, const SerializeInfo* info); + // Stores the ids of the current execution context stack. AliasedFloat64Array async_ids_stack_; // Attached to a Uint32Array that tracks the number of active hooks for @@ -737,6 +755,9 @@ class AsyncHooks : public MemoryRetainer { v8::Global js_execution_async_resources_; std::vector> native_execution_async_resources_; + + // Non-empty during deserialization + const SerializeInfo* info_ = nullptr; }; class ImmediateInfo : public MemoryRetainer { @@ -758,9 +779,16 @@ class ImmediateInfo : public MemoryRetainer { SET_SELF_SIZE(ImmediateInfo) void MemoryInfo(MemoryTracker* tracker) const override; + struct SerializeInfo { + AliasedBufferInfo fields; + }; + SerializeInfo Serialize(v8::Local context, + v8::SnapshotCreator* creator); + void Deserialize(v8::Local context); + private: friend class Environment; // So we can call the constructor. - inline explicit ImmediateInfo(v8::Isolate* isolate); + explicit ImmediateInfo(v8::Isolate* isolate, const SerializeInfo* info); enum Fields { kCount, kRefCount, kHasOutstanding, kFieldsCount }; @@ -783,9 +811,16 @@ class TickInfo : public MemoryRetainer { TickInfo& operator=(TickInfo&&) = delete; ~TickInfo() = default; + struct SerializeInfo { + AliasedBufferInfo fields; + }; + SerializeInfo Serialize(v8::Local context, + v8::SnapshotCreator* creator); + void Deserialize(v8::Local context); + private: friend class Environment; // So we can call the constructor. - inline explicit TickInfo(v8::Isolate* isolate); + explicit TickInfo(v8::Isolate* isolate, const SerializeInfo* info); enum Fields { kHasTickScheduled = 0, kHasRejectionToWarn, kFieldsCount }; @@ -857,6 +892,27 @@ class CleanupHookCallback { uint64_t insertion_order_counter_; }; +struct PropInfo { + std::string name; // name for debugging + size_t id; // In the list - in case there are any empty entires + SnapshotIndex index; // In the snapshot +}; + +struct EnvSerializeInfo { + AsyncHooks::SerializeInfo async_hooks; + TickInfo::SerializeInfo tick_info; + ImmediateInfo::SerializeInfo immediate_info; + performance::PerformanceState::SerializeInfo performance_state; + AliasedBufferInfo stream_base_state; + AliasedBufferInfo should_abort_on_uncaught_toggle; + + std::vector persistent_templates; + std::vector persistent_values; + + SnapshotIndex context; + friend std::ostream& operator<<(std::ostream& o, const EnvSerializeInfo& i); +}; + class Environment : public MemoryRetainer { public: Environment(const Environment&) = delete; @@ -870,7 +926,13 @@ class Environment : public MemoryRetainer { bool IsRootNode() const override { return true; } void MemoryInfo(MemoryTracker* tracker) const override; + EnvSerializeInfo Serialize(v8::SnapshotCreator* creator); void CreateProperties(); + void DeserializeProperties(const EnvSerializeInfo* info); + + typedef void (*BaseObjectIterator)(size_t, BaseObject*); + void ForEachBaseObject(BaseObjectIterator iterator); + void PrintAllBaseObjects(); // Should be called before InitializeInspector() void InitializeDiagnostics(); #if HAVE_INSPECTOR @@ -925,14 +987,17 @@ class Environment : public MemoryRetainer { v8::Isolate* isolate, const std::vector& args, const std::vector& exec_args, + const EnvSerializeInfo* env_info, EnvironmentFlags::Flags flags, ThreadId thread_id); - void InitializeMainContext(v8::Local context); + void InitializeMainContext(v8::Local context, + const EnvSerializeInfo* env_info); // Create an Environment and initialize the provided main context for it. Environment(IsolateData* isolate_data, v8::Local context, const std::vector& args, const std::vector& exec_args, + const EnvSerializeInfo* env_info, EnvironmentFlags::Flags flags, ThreadId thread_id); ~Environment() override; @@ -1354,6 +1419,7 @@ class Environment : public MemoryRetainer { AliasedInt32Array stream_base_state_; + uint64_t environment_start_time_; std::unique_ptr performance_state_; std::unordered_map performance_marks_; diff --git a/src/node.cc b/src/node.cc index e27edec0efe7d3..ce2834448e9b69 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1060,20 +1060,15 @@ int Start(int argc, char** argv) { { Isolate::CreateParams params; const std::vector* indexes = nullptr; - std::vector external_references; - + const EnvSerializeInfo* env_info = nullptr; bool force_no_snapshot = per_process::cli_options->per_isolate->no_node_snapshot; if (!force_no_snapshot) { v8::StartupData* blob = NodeMainInstance::GetEmbeddedSnapshotBlob(); if (blob != nullptr) { - // TODO(joyeecheung): collect external references and set it in - // params.external_references. - external_references = NodeMainInstance::CollectExternalReferences(); - external_references.push_back(reinterpret_cast(nullptr)); - params.external_references = external_references.data(); params.snapshot_blob = blob; indexes = NodeMainInstance::GetIsolateDataIndexes(); + env_info = NodeMainInstance::GetEnvSerializeInfo(); } } @@ -1083,7 +1078,7 @@ int Start(int argc, char** argv) { result.args, result.exec_args, indexes); - result.exit_code = main_instance.Run(); + result.exit_code = main_instance.Run(env_info); } TearDownOncePerProcess(); diff --git a/src/node_external_reference.h b/src/node_external_reference.h index 544236e3af669c..29dd4cb91ed663 100644 --- a/src/node_external_reference.h +++ b/src/node_external_reference.h @@ -46,7 +46,10 @@ class ExternalReferenceRegistry { std::vector external_references_; }; -#define EXTERNAL_REFERENCE_BINDING_LIST(V) +#define EXTERNAL_REFERENCE_BINDING_LIST_BASE(V) V(process_object) + +#define EXTERNAL_REFERENCE_BINDING_LIST(V) \ + EXTERNAL_REFERENCE_BINDING_LIST_BASE(V) } // namespace node diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc index 19f25574acbe95..9fefc371d66ab4 100644 --- a/src/node_main_instance.cc +++ b/src/node_main_instance.cc @@ -1,9 +1,9 @@ +#include "node_main_instance.h" +#include #include - -#include "node_errors.h" +#include "debug_utils-inl.h" #include "node_external_reference.h" #include "node_internals.h" -#include "node_main_instance.h" #include "node_options-inl.h" #include "node_v8_platform-inl.h" #include "util-inl.h" @@ -22,6 +22,7 @@ using v8::HandleScope; using v8::Isolate; using v8::Local; using v8::Locker; +using v8::Object; using v8::SealHandleScope; std::unique_ptr NodeMainInstance::registry_ = @@ -49,8 +50,6 @@ const std::vector& NodeMainInstance::CollectExternalReferences() { // Cannot be called more than once. CHECK_NULL(registry_); registry_.reset(new ExternalReferenceRegistry()); - - // TODO(joyeecheung): collect more external references here. return registry_->external_references(); } @@ -79,6 +78,15 @@ NodeMainInstance::NodeMainInstance( isolate_data_(nullptr), owns_isolate_(true) { params->array_buffer_allocator = array_buffer_allocator_.get(); + deserialize_mode_ = per_isolate_data_indexes != nullptr; + if (deserialize_mode_) { + // TODO(joyeecheung): collect external references and set it in + // params.external_references. + const std::vector& external_references = + CollectExternalReferences(); + params->external_references = external_references.data(); + } + isolate_ = Isolate::Allocate(); CHECK_NOT_NULL(isolate_); // Register the isolate on the platform before the isolate gets initialized, @@ -87,7 +95,6 @@ NodeMainInstance::NodeMainInstance( SetIsolateCreateParamsForNode(params); Isolate::Initialize(isolate_, *params); - deserialize_mode_ = per_isolate_data_indexes != nullptr; // If the indexes are not nullptr, we are not deserializing CHECK_IMPLIES(deserialize_mode_, params->external_references != nullptr); isolate_data_ = std::make_unique(isolate_, @@ -117,56 +124,57 @@ NodeMainInstance::~NodeMainInstance() { isolate_->Dispose(); } -int NodeMainInstance::Run() { +int NodeMainInstance::Run(const EnvSerializeInfo* env_info) { Locker locker(isolate_); Isolate::Scope isolate_scope(isolate_); HandleScope handle_scope(isolate_); int exit_code = 0; DeleteFnPtr env = - CreateMainEnvironment(&exit_code); + CreateMainEnvironment(&exit_code, env_info); CHECK_NOT_NULL(env); - Context::Scope context_scope(env->context()); + { + Context::Scope context_scope(env->context()); - if (exit_code == 0) { - LoadEnvironment(env.get()); + if (exit_code == 0) { + LoadEnvironment(env.get()); - env->set_trace_sync_io(env->options()->trace_sync_io); + env->set_trace_sync_io(env->options()->trace_sync_io); - { - SealHandleScope seal(isolate_); - bool more; - env->performance_state()->Mark( - node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_START); - do { - uv_run(env->event_loop(), UV_RUN_DEFAULT); + { + SealHandleScope seal(isolate_); + bool more; + env->performance_state()->Mark( + node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_START); + do { + uv_run(env->event_loop(), UV_RUN_DEFAULT); - per_process::v8_platform.DrainVMTasks(isolate_); + per_process::v8_platform.DrainVMTasks(isolate_); - more = uv_loop_alive(env->event_loop()); - if (more && !env->is_stopping()) continue; + more = uv_loop_alive(env->event_loop()); + if (more && !env->is_stopping()) continue; - if (!uv_loop_alive(env->event_loop())) { - EmitBeforeExit(env.get()); - } + if (!uv_loop_alive(env->event_loop())) { + EmitBeforeExit(env.get()); + } - // Emit `beforeExit` if the loop became alive either after emitting - // event, or after running some callbacks. - more = uv_loop_alive(env->event_loop()); - } while (more == true && !env->is_stopping()); - env->performance_state()->Mark( - node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_EXIT); - } + // Emit `beforeExit` if the loop became alive either after emitting + // event, or after running some callbacks. + more = uv_loop_alive(env->event_loop()); + } while (more == true && !env->is_stopping()); + env->performance_state()->Mark( + node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_EXIT); + } - env->set_trace_sync_io(false); - exit_code = EmitExit(env.get()); - } + env->set_trace_sync_io(false); + exit_code = EmitExit(env.get()); + } - ResetStdio(); + ResetStdio(); - // TODO(addaleax): Neither NODE_SHARED_MODE nor HAVE_INSPECTOR really - // make sense here. + // TODO(addaleax): Neither NODE_SHARED_MODE nor HAVE_INSPECTOR really + // make sense here. #if HAVE_INSPECTOR && defined(__POSIX__) && !defined(NODE_SHARED_MODE) struct sigaction act; memset(&act, 0, sizeof(act)); @@ -181,12 +189,26 @@ int NodeMainInstance::Run() { #if defined(LEAK_SANITIZER) __lsan_do_leak_check(); #endif + } return exit_code; } +void DeserializeNodeInternalFields(Local holder, + int index, + v8::StartupData payload, + void* env) { + if (payload.raw_size == 0) { + holder->SetAlignedPointerInInternalField(index, nullptr); + return; + } + // No embedder object in the builtin snapshot yet. + UNREACHABLE(); +} + DeleteFnPtr -NodeMainInstance::CreateMainEnvironment(int* exit_code) { +NodeMainInstance::CreateMainEnvironment(int* exit_code, + const EnvSerializeInfo* env_info) { *exit_code = 0; // Reset the exit code to 0 HandleScope handle_scope(isolate_); @@ -197,32 +219,48 @@ NodeMainInstance::CreateMainEnvironment(int* exit_code) { isolate_->GetHeapProfiler()->StartTrackingHeapObjects(true); } + CHECK_IMPLIES(deserialize_mode_, env_info != nullptr); Local context; + DeleteFnPtr env; + if (deserialize_mode_) { - context = - Context::FromSnapshot(isolate_, kNodeContextIndex).ToLocalChecked(); + env.reset(new Environment(isolate_data_.get(), + isolate_, + args_, + exec_args_, + env_info, + EnvironmentFlags::kDefaultFlags, + {})); + context = Context::FromSnapshot(isolate_, + kNodeContextIndex, + {DeserializeNodeInternalFields, env.get()}) + .ToLocalChecked(); + InitializeContextRuntime(context); SetIsolateErrorHandlers(isolate_, {}); } else { context = NewContext(isolate_); + Context::Scope context_scope(context); + env.reset(new Environment(isolate_data_.get(), + context, + args_, + exec_args_, + nullptr, + EnvironmentFlags::kDefaultFlags, + {})); } CHECK(!context.IsEmpty()); Context::Scope context_scope(context); - DeleteFnPtr env { CreateEnvironment( - isolate_data_.get(), - context, - args_, - exec_args_, - EnvironmentFlags::kDefaultFlags) }; + env->InitializeMainContext(context, env_info); - if (*exit_code != 0) { - return env; - } +#if HAVE_INSPECTOR + env->InitializeInspector({}); +#endif - if (env == nullptr) { - *exit_code = 1; + if (env->RunBootstrapping().IsEmpty()) { + return nullptr; } return env; diff --git a/src/node_main_instance.h b/src/node_main_instance.h index de1f4dca2b7cc6..6e38e95c26635c 100644 --- a/src/node_main_instance.h +++ b/src/node_main_instance.h @@ -14,6 +14,7 @@ namespace node { class ExternalReferenceRegistry; +struct EnvSerializeInfo; // TODO(joyeecheung): align this with the Worker/WorkerThreadData class. // We may be able to create an abstract class to reuse some of the routines. @@ -57,17 +58,18 @@ class NodeMainInstance { ~NodeMainInstance(); // Start running the Node.js instances, return the exit code when finished. - int Run(); + int Run(const EnvSerializeInfo* env_info); IsolateData* isolate_data() { return isolate_data_.get(); } DeleteFnPtr CreateMainEnvironment( - int* exit_code); + int* exit_code, const EnvSerializeInfo* env_info); // If nullptr is returned, the binary is not built with embedded // snapshot. static const std::vector* GetIsolateDataIndexes(); static v8::StartupData* GetEmbeddedSnapshotBlob(); + static const EnvSerializeInfo* GetEnvSerializeInfo(); static const std::vector& CollectExternalReferences(); static const size_t kNodeContextIndex = 0; diff --git a/src/node_perf.cc b/src/node_perf.cc index b5efc05689f20c..d71afc2d81c258 100644 --- a/src/node_perf.cc +++ b/src/node_perf.cc @@ -40,6 +40,52 @@ const uint64_t timeOrigin = PERFORMANCE_NOW(); const double timeOriginTimestamp = GetCurrentTimeInMicroseconds(); uint64_t performance_v8_start; +PerformanceState::PerformanceState(Isolate* isolate, + const PerformanceState::SerializeInfo* info) + : root(isolate, + sizeof(performance_state_internal), + MAYBE_FIELD_PTR(info, root)), + milestones(isolate, + offsetof(performance_state_internal, milestones), + NODE_PERFORMANCE_MILESTONE_INVALID, + root, + MAYBE_FIELD_PTR(info, milestones)), + observers(isolate, + offsetof(performance_state_internal, observers), + NODE_PERFORMANCE_ENTRY_TYPE_INVALID, + root, + MAYBE_FIELD_PTR(info, observers)) { + if (info == nullptr) { + for (size_t i = 0; i < milestones.Length(); i++) milestones[i] = -1.; + } +} + +PerformanceState::SerializeInfo PerformanceState::Serialize( + v8::Local context, v8::SnapshotCreator* creator) { + SerializeInfo info{root.Serialize(context, creator), + milestones.Serialize(context, creator), + observers.Serialize(context, creator)}; + return info; +} + +void PerformanceState::Deserialize(v8::Local context) { + root.Deserialize(context); + // This is just done to set up the pointers, we will actually reset + // all the milestones after deserialization. + milestones.Deserialize(context); + observers.Deserialize(context); +} + +std::ostream& operator<<(std::ostream& o, + const PerformanceState::SerializeInfo& i) { + o << "{\n" + << " " << i.root << ", // root\n" + << " " << i.milestones << ", // milestones\n" + << " " << i.observers << ", // observers\n" + << "}"; + return o; +} + void PerformanceState::Mark(enum PerformanceMilestone milestone, uint64_t ts) { this->milestones[milestone] = ts; @@ -111,8 +157,8 @@ void PerformanceEntry::Notify(Environment* env, Local object) { Context::Scope scope(env->context()); AliasedUint32Array& observers = env->performance_state()->observers; - if (type != NODE_PERFORMANCE_ENTRY_TYPE_INVALID && - observers[type]) { + if (!env->performance_entry_callback().IsEmpty() && + type != NODE_PERFORMANCE_ENTRY_TYPE_INVALID && observers[type]) { node::MakeCallback(env->isolate(), object.As(), env->performance_entry_callback(), diff --git a/src/node_perf_common.h b/src/node_perf_common.h index 75d266afc257e9..6c4c98813e71f0 100644 --- a/src/node_perf_common.h +++ b/src/node_perf_common.h @@ -8,6 +8,7 @@ #include "v8.h" #include +#include #include #include @@ -54,23 +55,17 @@ enum PerformanceEntryType { class PerformanceState { public: - explicit PerformanceState(v8::Isolate* isolate) : - root( - isolate, - sizeof(performance_state_internal)), - milestones( - isolate, - offsetof(performance_state_internal, milestones), - NODE_PERFORMANCE_MILESTONE_INVALID, - root), - observers( - isolate, - offsetof(performance_state_internal, observers), - NODE_PERFORMANCE_ENTRY_TYPE_INVALID, - root) { - for (size_t i = 0; i < milestones.Length(); i++) - milestones[i] = -1.; - } + struct SerializeInfo { + AliasedBufferInfo root; + AliasedBufferInfo milestones; + AliasedBufferInfo observers; + }; + + explicit PerformanceState(v8::Isolate* isolate, const SerializeInfo* info); + SerializeInfo Serialize(v8::Local context, + v8::SnapshotCreator* creator); + void Deserialize(v8::Local context); + friend std::ostream& operator<<(std::ostream& o, const SerializeInfo& i); AliasedUint8Array root; AliasedFloat64Array milestones; diff --git a/src/node_process_object.cc b/src/node_process_object.cc index 5bec65805a1024..4648cfbd11a925 100644 --- a/src/node_process_object.cc +++ b/src/node_process_object.cc @@ -1,7 +1,8 @@ #include "env-inl.h" +#include "node_external_reference.h" #include "node_internals.h" -#include "node_options-inl.h" #include "node_metadata.h" +#include "node_options-inl.h" #include "node_process.h" #include "node_revert.h" #include "util-inl.h" @@ -200,4 +201,11 @@ void PatchProcessObject(const FunctionCallbackInfo& args) { .FromJust()); } +void RegisterProcessExternalReferences(ExternalReferenceRegistry* registry) { + registry->Register(RawDebug); +} + } // namespace node + +NODE_MODULE_EXTERNAL_REFERENCE(process_object, + node::RegisterProcessExternalReferences) diff --git a/src/node_snapshot_stub.cc b/src/node_snapshot_stub.cc index fac03b0c87af5d..9d7b085994bf9f 100644 --- a/src/node_snapshot_stub.cc +++ b/src/node_snapshot_stub.cc @@ -14,4 +14,8 @@ const std::vector* NodeMainInstance::GetIsolateDataIndexes() { return nullptr; } +const EnvSerializeInfo* NodeMainInstance::GetEnvSerializeInfo() { + return nullptr; +} + } // namespace node diff --git a/src/util.h b/src/util.h index 8b8a63adca9f93..71f3886365e28a 100644 --- a/src/util.h +++ b/src/util.h @@ -805,6 +805,7 @@ std::unique_ptr static_unique_pointer_cast(std::unique_ptr&& ptr) { return std::unique_ptr(static_cast(ptr.release())); } +#define MAYBE_FIELD_PTR(ptr, field) ptr == nullptr ? nullptr : &(ptr->field) } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/tools/snapshot/snapshot_builder.cc b/tools/snapshot/snapshot_builder.cc index 7d292391ab334a..7086a98a6c80c8 100644 --- a/tools/snapshot/snapshot_builder.cc +++ b/tools/snapshot/snapshot_builder.cc @@ -1,6 +1,7 @@ #include "snapshot_builder.h" #include #include +#include "debug_utils-inl.h" #include "env-inl.h" #include "node_external_reference.h" #include "node_internals.h" @@ -12,6 +13,8 @@ namespace node { using v8::Context; using v8::HandleScope; using v8::Isolate; +using v8::Local; +using v8::Object; using v8::SnapshotCreator; using v8::StartupData; @@ -23,10 +26,12 @@ void WriteVector(std::stringstream* ss, const T* vec, size_t size) { } std::string FormatBlob(v8::StartupData* blob, - const std::vector& isolate_data_indexes) { + const std::vector& isolate_data_indexes, + const EnvSerializeInfo& env_info) { std::stringstream ss; ss << R"(#include +#include "env.h" #include "node_main_instance.h" #include "v8.h" @@ -56,17 +61,39 @@ static const std::vector isolate_data_indexes { const std::vector* NodeMainInstance::GetIsolateDataIndexes() { return &isolate_data_indexes; } + +static const EnvSerializeInfo env_info )" + << env_info << R"(; + +const EnvSerializeInfo* NodeMainInstance::GetEnvSerializeInfo() { + return &env_info; +} + } // namespace node )"; return ss.str(); } +static v8::StartupData SerializeNodeContextInternalFields(Local holder, + int index, + void* env) { + void* ptr = holder->GetAlignedPointerFromInternalField(index); + if (ptr == nullptr || ptr == env) { + return StartupData{nullptr, 0}; + } + if (ptr == env && index == ContextEmbedderIndex::kEnvironment) { + return StartupData{nullptr, 0}; + } + + // No embedder objects in the builtin snapshot yet. + UNREACHABLE(); + return StartupData{nullptr, 0}; +} + std::string SnapshotBuilder::Generate( const std::vector args, const std::vector exec_args) { - const std::vector& external_references = - NodeMainInstance::CollectExternalReferences(); Isolate* isolate = Isolate::Allocate(); per_process::v8_platform.Platform()->RegisterIsolate(isolate, uv_default_loop()); @@ -75,7 +102,12 @@ std::string SnapshotBuilder::Generate( { std::vector isolate_data_indexes; + EnvSerializeInfo env_info; + + const std::vector& external_references = + NodeMainInstance::CollectExternalReferences(); SnapshotCreator creator(isolate, external_references.data()); + Environment* env; { main_instance = NodeMainInstance::Create(isolate, @@ -83,11 +115,28 @@ std::string SnapshotBuilder::Generate( per_process::v8_platform.Platform(), args, exec_args); + HandleScope scope(isolate); creator.SetDefaultContext(Context::New(isolate)); isolate_data_indexes = main_instance->isolate_data()->Serialize(&creator); - size_t index = creator.AddContext(NewContext(isolate)); + Local context = NewContext(isolate); + Context::Scope context_scope(context); + + env = new Environment(main_instance->isolate_data(), + context, + args, + exec_args, + nullptr, + node::EnvironmentFlags::kDefaultFlags, + {}); + if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) { + env->PrintAllBaseObjects(); + printf("Environment = %p\n", env); + } + env_info = env->Serialize(&creator); + size_t index = creator.AddContext( + context, {SerializeNodeContextInternalFields, env}); CHECK_EQ(index, NodeMainInstance::kNodeContextIndex); } @@ -97,8 +146,9 @@ std::string SnapshotBuilder::Generate( CHECK(blob.CanBeRehashed()); // Must be done while the snapshot creator isolate is entered i.e. the // creator is still alive. + FreeEnvironment(env); main_instance->Dispose(); - result = FormatBlob(&blob, isolate_data_indexes); + result = FormatBlob(&blob, isolate_data_indexes, env_info); delete[] blob.data; }