Skip to content

Commit 40ae03a

Browse files
committed
src: snapshot Environment upon instantiation
1 parent 720801a commit 40ae03a

14 files changed

Lines changed: 748 additions & 187 deletions

src/aliased_buffer.h

Lines changed: 76 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
namespace node {
1212

13+
typedef size_t AliasedBufferInfo;
14+
1315
/**
1416
* Do not use this class directly when creating instances of it - use the
1517
* Aliased*Array defined at the end of this file instead.
@@ -28,7 +30,14 @@ namespace node {
2830
* observed. Any notification APIs will be left as a future exercise.
2931
*/
3032

31-
enum class AliasedBufferType { kNew, kCopy, kShared, kAssigned };
33+
enum class AliasedBufferType {
34+
kNew,
35+
kCopy,
36+
kShared,
37+
kAssigned,
38+
kToBeDeserialized,
39+
kDeserialized
40+
};
3241

3342
template <class NativeT,
3443
class V8T,
@@ -38,25 +47,51 @@ class AliasedBufferBase {
3847
public:
3948
static std::unordered_set<AliasedBufferBase*> buffers;
4049

41-
inline AliasedBufferBase(v8::Isolate* isolate, const size_t count)
50+
inline AliasedBufferBase(v8::Isolate* isolate,
51+
const size_t count,
52+
const AliasedBufferInfo* info = nullptr)
4253
: isolate_(isolate),
4354
count_(count),
4455
byte_offset_(0),
4556
type_(AliasedBufferType::kNew) {
4657
buffers.insert(this);
47-
CHECK_GT(count, 0);
4858
const v8::HandleScope handle_scope(isolate_);
49-
const size_t size_in_bytes =
50-
MultiplyWithOverflowCheck(sizeof(NativeT), count);
59+
if (info == nullptr) {
60+
CHECK_GT(count, 0);
61+
const size_t size_in_bytes =
62+
MultiplyWithOverflowCheck(sizeof(NativeT), count);
63+
64+
// allocate v8 ArrayBuffer
65+
v8::Local<v8::ArrayBuffer> ab =
66+
v8::ArrayBuffer::New(isolate_, size_in_bytes);
67+
buffer_ = static_cast<NativeT*>(ab->GetBackingStore()->Data());
68+
69+
// allocate v8 TypedArray
70+
v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, count);
71+
js_array_ = v8::Global<V8T>(isolate, js_array);
72+
} else {
73+
// To be deserialized later when context is available
74+
type_ = AliasedBufferType::kToBeDeserialized;
75+
info_ = info;
76+
buffer_ = nullptr;
77+
}
78+
}
5179

52-
// allocate v8 ArrayBuffer
53-
v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(
54-
isolate_, size_in_bytes);
55-
buffer_ = static_cast<NativeT*>(ab->GetBackingStore()->Data());
80+
AliasedBufferInfo Serialize(v8::Local<v8::Context> context,
81+
v8::SnapshotCreator* creator) {
82+
return creator->AddData(context, GetJSArray());
83+
}
5684

57-
// allocate v8 TypedArray
58-
v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, count);
59-
js_array_ = v8::Global<V8T>(isolate, js_array);
85+
inline void Deserialize(v8::Local<v8::Context> context) {
86+
DCHECK_EQ(type_, AliasedBufferType::kToBeDeserialized);
87+
v8::Local<V8T> arr =
88+
context->GetDataFromSnapshotOnce<V8T>(*info_).ToLocalChecked();
89+
DCHECK_EQ(count_, arr->Length());
90+
byte_offset_ = arr->ByteOffset();
91+
buffer_ = static_cast<NativeT*>(arr->Buffer()->GetBackingStore()->Data());
92+
js_array_.Reset(isolate_, arr);
93+
info_ = nullptr;
94+
type_ = AliasedBufferType::kDeserialized;
6095
}
6196

6297
/**
@@ -80,19 +115,34 @@ class AliasedBufferBase {
80115
buffers.insert(this);
81116
const v8::HandleScope handle_scope(isolate_);
82117

118+
if (backing_buffer.type() == AliasedBufferType::kToBeDeserialized) {
119+
type_ = AliasedBufferType::kToBeDeserialized;
120+
buffer_ = nullptr;
121+
return;
122+
}
123+
CreateView(backing_buffer);
124+
}
125+
126+
inline void CreateView(
127+
const AliasedBufferBase<uint8_t, v8::Uint8Array>& backing_buffer) {
83128
v8::Local<v8::ArrayBuffer> ab = backing_buffer.GetArrayBuffer();
84129

85130
// validate that the byte_offset is aligned with sizeof(NativeT)
86-
CHECK_EQ(byte_offset & (sizeof(NativeT) - 1), 0);
131+
CHECK_EQ(byte_offset_ & (sizeof(NativeT) - 1), 0);
87132
// validate this fits inside the backing buffer
88-
CHECK_LE(MultiplyWithOverflowCheck(sizeof(NativeT), count),
89-
ab->ByteLength() - byte_offset);
133+
CHECK_LE(MultiplyWithOverflowCheck(sizeof(NativeT), count_),
134+
ab->ByteLength() - byte_offset_);
90135

91136
buffer_ = reinterpret_cast<NativeT*>(
92-
const_cast<uint8_t*>(backing_buffer.GetNativeBuffer() + byte_offset));
137+
const_cast<uint8_t*>(backing_buffer.GetNativeBuffer() + byte_offset_));
138+
139+
v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, count_);
140+
js_array_ = v8::Global<V8T>(isolate_, js_array);
93141

94-
v8::Local<V8T> js_array = V8T::New(ab, byte_offset, count);
95-
js_array_ = v8::Global<V8T>(isolate, js_array);
142+
if (type_ == AliasedBufferType::kToBeDeserialized) {
143+
type_ = AliasedBufferType::kShared;
144+
info_ = nullptr;
145+
}
96146
}
97147

98148
AliasedBufferBase(const AliasedBufferBase& that)
@@ -102,12 +152,14 @@ class AliasedBufferBase {
102152
buffer_(that.buffer_),
103153
type_(AliasedBufferType::kCopy) {
104154
buffers.insert(this);
155+
DCHECK_NE(type_, AliasedBufferType::kToBeDeserialized);
105156
js_array_ = v8::Global<V8T>(that.isolate_, that.GetJSArray());
106157
}
107158

108159
inline ~AliasedBufferBase() { buffers.erase(this); }
109160

110161
AliasedBufferBase& operator=(AliasedBufferBase&& that) noexcept {
162+
DCHECK_NE(type_, AliasedBufferType::kToBeDeserialized);
111163
this->~AliasedBufferBase();
112164
isolate_ = that.isolate_;
113165
count_ = that.count_;
@@ -174,6 +226,7 @@ class AliasedBufferBase {
174226
* Get the underlying v8 TypedArray overlayed on top of the native buffer
175227
*/
176228
v8::Local<V8T> GetJSArray() const {
229+
DCHECK_NE(type_, AliasedBufferType::kToBeDeserialized);
177230
return js_array_.Get(isolate_);
178231
}
179232

@@ -190,6 +243,7 @@ class AliasedBufferBase {
190243
* through the GetValue/SetValue/operator[] methods
191244
*/
192245
inline const NativeT* GetNativeBuffer() const {
246+
DCHECK_NE(type_, AliasedBufferType::kToBeDeserialized);
193247
return buffer_;
194248
}
195249

@@ -205,13 +259,15 @@ class AliasedBufferBase {
205259
*/
206260
inline void SetValue(const size_t index, NativeT value) {
207261
DCHECK_LT(index, count_);
262+
DCHECK_NE(type_, AliasedBufferType::kToBeDeserialized);
208263
buffer_[index] = value;
209264
}
210265

211266
/**
212267
* Get value at position index
213268
*/
214269
inline const NativeT GetValue(const size_t index) const {
270+
DCHECK_NE(type_, AliasedBufferType::kToBeDeserialized);
215271
DCHECK_LT(index, count_);
216272
return buffer_[index];
217273
}
@@ -220,6 +276,7 @@ class AliasedBufferBase {
220276
* Effectively, a synonym for GetValue/SetValue
221277
*/
222278
Reference operator[](size_t index) {
279+
DCHECK_NE(type_, AliasedBufferType::kToBeDeserialized);
223280
return Reference(this, index);
224281
}
225282

@@ -279,6 +336,7 @@ class AliasedBufferBase {
279336
NativeT* buffer_;
280337
v8::Global<V8T> js_array_;
281338
AliasedBufferType type_;
339+
const AliasedBufferInfo* info_ = nullptr;
282340
};
283341

284342
template <class NativeT, class V8T, typename Trait>

src/api/environment.cc

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -369,15 +369,7 @@ Environment* CreateEnvironment(
369369
// TODO(addaleax): This is a much better place for parsing per-Environment
370370
// options than the global parse call.
371371
Environment* env = new Environment(
372-
isolate_data,
373-
context,
374-
args,
375-
exec_args,
376-
flags,
377-
thread_id);
378-
if (flags & EnvironmentFlags::kOwnsProcessState) {
379-
env->set_abort_on_uncaught_exception(false);
380-
}
372+
isolate_data, context, args, exec_args, nullptr, flags, thread_id);
381373

382374
#if HAVE_INSPECTOR
383375
if (inspector_parent_handle) {

src/base_object.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030

3131
namespace node {
3232

33+
enum class InternalFieldType { kDefault = 0, kNoBindingData };
34+
3335
class Environment;
3436
template <typename T, bool kIsWeak>
3537
class BaseObjectPtrImpl;
@@ -98,10 +100,14 @@ class BaseObject : public MemoryRetainer {
98100
// a BaseObjectPtr to this object.
99101
inline void Detach();
100102

103+
InternalFieldType type() { return type_; }
104+
void set_type(InternalFieldType type) { type_ = type; }
105+
101106
protected:
102107
virtual inline void OnGCCollect();
103108

104109
private:
110+
InternalFieldType type_ = InternalFieldType::kDefault;
105111
v8::Local<v8::Object> WrappedObject() const override;
106112
static void DeleteMe(void* data);
107113

src/debug_utils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ void FWrite(FILE* file, const std::string& str);
4545
V(INSPECTOR_SERVER) \
4646
V(INSPECTOR_PROFILER) \
4747
V(CODE_CACHE) \
48-
V(WASI)
48+
V(WASI) \
49+
V(MKSNAPSHOT)
4950

5051
enum class DebugCategory {
5152
#define V(name) name,

src/env-inl.h

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -77,29 +77,6 @@ inline v8::Local<v8::String> IsolateData::async_wrap_provider(int index) const {
7777
return async_wrap_providers_[index].Get(isolate_);
7878
}
7979

80-
inline AsyncHooks::AsyncHooks()
81-
: async_ids_stack_(env()->isolate(), 16 * 2),
82-
fields_(env()->isolate(), kFieldsCount),
83-
async_id_fields_(env()->isolate(), kUidFieldsCount) {
84-
clear_async_id_stack();
85-
86-
// Always perform async_hooks checks, not just when async_hooks is enabled.
87-
// TODO(AndreasMadsen): Consider removing this for LTS releases.
88-
// See discussion in https://github.com/nodejs/node/pull/15454
89-
// When removing this, do it by reverting the commit. Otherwise the test
90-
// and flag changes won't be included.
91-
fields_[kCheck] = 1;
92-
93-
// kDefaultTriggerAsyncId should be -1, this indicates that there is no
94-
// specified default value and it should fallback to the executionAsyncId.
95-
// 0 is not used as the magic value, because that indicates a missing context
96-
// which is different from a default context.
97-
async_id_fields_[AsyncHooks::kDefaultTriggerAsyncId] = -1;
98-
99-
// kAsyncIdCounter should start at 1 because that'll be the id the execution
100-
// context during bootstrap (code that runs before entering uv_run()).
101-
async_id_fields_[AsyncHooks::kAsyncIdCounter] = 1;
102-
}
10380
inline AliasedUint32Array& AsyncHooks::fields() {
10481
return fields_;
10582
}
@@ -238,9 +215,6 @@ inline void Environment::PopAsyncCallbackScope() {
238215
async_callback_scope_depth_--;
239216
}
240217

241-
inline ImmediateInfo::ImmediateInfo(v8::Isolate* isolate)
242-
: fields_(isolate, kFieldsCount) {}
243-
244218
inline AliasedUint32Array& ImmediateInfo::fields() {
245219
return fields_;
246220
}
@@ -265,9 +239,6 @@ inline void ImmediateInfo::ref_count_dec(uint32_t decrement) {
265239
fields_[kRefCount] -= decrement;
266240
}
267241

268-
inline TickInfo::TickInfo(v8::Isolate* isolate)
269-
: fields_(isolate, kFieldsCount) {}
270-
271242
inline AliasedUint8Array& TickInfo::fields() {
272243
return fields_;
273244
}
@@ -365,16 +336,22 @@ inline v8::Local<v8::Uint32> BindingDataBase::New(
365336
Environment* env,
366337
v8::Local<v8::Context> context,
367338
v8::Local<v8::Object> target) {
368-
T* data = new T(env, target);
339+
return Initialize(context, new T(env, target));
340+
}
341+
342+
template <typename T>
343+
inline v8::Local<v8::Uint32> BindingDataBase::Initialize(
344+
v8::Local<v8::Context> context, T* binding) {
369345
// This won't compile if T is not a BindingDataBase subclass.
370-
BindingDataBase* item = static_cast<BindingDataBase*>(data);
346+
BindingDataBase* item = static_cast<BindingDataBase*>(binding);
371347
std::vector<BindingDataBase*>* list =
372348
static_cast<std::vector<BindingDataBase*>*>(
373349
context->GetAlignedPointerFromEmbedderData(
374350
ContextEmbedderIndex::KBindingListIndex));
375351
size_t index = list->size();
376352
list->push_back(item);
377-
return v8::Integer::NewFromUnsigned(env->isolate(), index).As<v8::Uint32>();
353+
return v8::Integer::NewFromUnsigned(context->GetIsolate(), index)
354+
.As<v8::Uint32>();
378355
}
379356

380357
template <typename T>
@@ -384,7 +361,7 @@ Environment::BindingScope<T>::BindingScope(Environment* env,
384361
: env(env) {
385362
v8::Local<v8::Uint32> index = BindingDataBase::New<T>(env, context, target);
386363
data = BindingDataBase::Unwrap<T>(context, index);
387-
env->set_current_callback_data(index);
364+
env->set_current_callback_data(index.As<v8::Uint32>());
388365
}
389366

390367
template <typename T>

0 commit comments

Comments
 (0)