Skip to content

Commit

Permalink
src: snapshot Environment upon instantiation
Browse files Browse the repository at this point in the history
  • Loading branch information
joyeecheung committed May 5, 2020
1 parent 94335ab commit 7d0932e
Show file tree
Hide file tree
Showing 14 changed files with 842 additions and 185 deletions.
93 changes: 72 additions & 21 deletions src/aliased_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include <cinttypes>
#include <iostream>
#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.
Expand All @@ -26,27 +29,53 @@ namespace node {
* The encapsulation herein provides a placeholder where such writes can be
* observed. Any notification APIs will be left as a future exercise.
*/

template <class NativeT,
class V8T,
// SFINAE NativeT to be scalar
typename = std::enable_if_t<std::is_scalar<NativeT>::value>>
class AliasedBufferBase {
public:
AliasedBufferBase(v8::Isolate* isolate, const size_t count)
AliasedBufferBase(v8::Isolate* isolate,
const size_t count,
const AliasedBufferInfo* info = nullptr)
: isolate_(isolate), count_(count), byte_offset_(0) {
CHECK_GT(count, 0);
const v8::HandleScope handle_scope(isolate_);
const size_t size_in_bytes =
MultiplyWithOverflowCheck(sizeof(NativeT), count);
if (info == nullptr) {
CHECK_GT(count, 0);
const size_t size_in_bytes =
MultiplyWithOverflowCheck(sizeof(NativeT), count);

// allocate v8 ArrayBuffer
v8::Local<v8::ArrayBuffer> ab =
v8::ArrayBuffer::New(isolate_, size_in_bytes);
buffer_ = static_cast<NativeT*>(ab->GetBackingStore()->Data());

// allocate v8 TypedArray
v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, count);
js_array_ = v8::Global<V8T>(isolate, js_array);
} else {
info_ = info;
buffer_ = nullptr;
}
}

// allocate v8 ArrayBuffer
v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(
isolate_, size_in_bytes);
buffer_ = static_cast<NativeT*>(ab->GetBackingStore()->Data());
AliasedBufferInfo Serialize(v8::Local<v8::Context> context,
v8::SnapshotCreator* creator) {
return creator->AddData(context, GetJSArray());
}

// allocate v8 TypedArray
v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, count);
js_array_ = v8::Global<V8T>(isolate, js_array);
inline void Deserialize(v8::Local<v8::Context> context) {
v8::Local<V8T> arr =
context->GetDataFromSnapshotOnce<V8T>(*info_).ToLocalChecked();
CHECK_EQ(count_, arr->Length());
CHECK_EQ(byte_offset_, arr->ByteOffset());
uint8_t* raw =
static_cast<uint8_t*>(arr->Buffer()->GetBackingStore()->Data());
buffer_ = reinterpret_cast<NativeT*>(raw + byte_offset_);
js_array_.Reset(isolate_, arr);
info_ = nullptr;
}

/**
Expand All @@ -62,23 +91,29 @@ class AliasedBufferBase {
v8::Isolate* isolate,
const size_t byte_offset,
const size_t count,
const AliasedBufferBase<uint8_t, v8::Uint8Array>& backing_buffer)
const AliasedBufferBase<uint8_t, v8::Uint8Array>& backing_buffer,
const AliasedBufferInfo* info = nullptr)
: isolate_(isolate), count_(count), byte_offset_(byte_offset) {
const v8::HandleScope handle_scope(isolate_);

v8::Local<v8::ArrayBuffer> ab = backing_buffer.GetArrayBuffer();
if (info == nullptr) {
v8::Local<v8::ArrayBuffer> ab = backing_buffer.GetArrayBuffer();

// validate that the byte_offset is aligned with sizeof(NativeT)
CHECK_EQ(byte_offset & (sizeof(NativeT) - 1), 0);
// validate this fits inside the backing buffer
CHECK_LE(MultiplyWithOverflowCheck(sizeof(NativeT), count),
ab->ByteLength() - byte_offset);
// validate that the byte_offset is aligned with sizeof(NativeT)
CHECK_EQ(byte_offset_ & (sizeof(NativeT) - 1), 0);
// validate this fits inside the backing buffer
CHECK_LE(MultiplyWithOverflowCheck(sizeof(NativeT), count_),
ab->ByteLength() - byte_offset_);

buffer_ = reinterpret_cast<NativeT*>(
const_cast<uint8_t*>(backing_buffer.GetNativeBuffer() + byte_offset));
buffer_ = reinterpret_cast<NativeT*>(const_cast<uint8_t*>(
backing_buffer.GetNativeBuffer() + byte_offset_));

v8::Local<V8T> js_array = V8T::New(ab, byte_offset, count);
js_array_ = v8::Global<V8T>(isolate, js_array);
v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, count_);
js_array_ = v8::Global<V8T>(isolate_, js_array);
} else {
info_ = info;
buffer_ = nullptr;
}
}

AliasedBufferBase(const AliasedBufferBase& that)
Expand All @@ -90,6 +125,7 @@ class AliasedBufferBase {
}

AliasedBufferBase& operator=(AliasedBufferBase&& that) noexcept {
DCHECK_NULL(info_);
this->~AliasedBufferBase();
isolate_ = that.isolate_;
count_ = that.count_;
Expand Down Expand Up @@ -155,6 +191,7 @@ class AliasedBufferBase {
* Get the underlying v8 TypedArray overlayed on top of the native buffer
*/
v8::Local<V8T> GetJSArray() const {
DCHECK_NULL(info_);
return js_array_.Get(isolate_);
}

Expand All @@ -171,6 +208,7 @@ class AliasedBufferBase {
* through the GetValue/SetValue/operator[] methods
*/
inline const NativeT* GetNativeBuffer() const {
DCHECK_NULL(info_);
return buffer_;
}

Expand All @@ -186,13 +224,15 @@ class AliasedBufferBase {
*/
inline void SetValue(const size_t index, NativeT value) {
DCHECK_LT(index, count_);
DCHECK_NULL(info_);
buffer_[index] = value;
}

/**
* Get value at position index
*/
inline const NativeT GetValue(const size_t index) const {
DCHECK_NULL(info_);
DCHECK_LT(index, count_);
return buffer_[index];
}
Expand All @@ -201,6 +241,7 @@ class AliasedBufferBase {
* Effectively, a synonym for GetValue/SetValue
*/
Reference operator[](size_t index) {
DCHECK_NULL(info_);
return Reference(this, index);
}

Expand Down Expand Up @@ -243,12 +284,22 @@ class AliasedBufferBase {
count_ = new_capacity;
}

void Print() {
std::cout << "[ ";
for (size_t i = 0; i < count_; ++i) {
std::cout << std::to_string(buffer_[i])
<< (i == count_ - 1 ? " ]\n" : ", ");
}
}

private:
v8::Isolate* isolate_;
size_t count_;
size_t byte_offset_;
NativeT* buffer_;
v8::Global<V8T> js_array_;

const AliasedBufferInfo* info_ = nullptr;
};

typedef AliasedBufferBase<int32_t, v8::Int32Array> AliasedInt32Array;
Expand Down
10 changes: 1 addition & 9 deletions src/api/environment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -368,15 +368,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);
if (flags & EnvironmentFlags::kOwnsProcessState) {
env->set_abort_on_uncaught_exception(false);
}
isolate_data, context, args, exec_args, nullptr, flags, thread_id);

#if HAVE_INSPECTOR
if (inspector_parent_handle) {
Expand Down
6 changes: 6 additions & 0 deletions src/base_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

namespace node {

enum class InternalFieldType { kDefault = 0, kNoBindingData };

class Environment;
template <typename T, bool kIsWeak>
class BaseObjectPtrImpl;
Expand Down Expand Up @@ -98,10 +100,14 @@ class BaseObject : public MemoryRetainer {
// a BaseObjectPtr to this object.
inline void Detach();

InternalFieldType type() { return type_; }
void set_type(InternalFieldType type) { type_ = type; }

protected:
virtual inline void OnGCCollect();

private:
InternalFieldType type_ = InternalFieldType::kDefault;
v8::Local<v8::Object> WrappedObject() const override;
static void DeleteMe(void* data);

Expand Down
3 changes: 2 additions & 1 deletion src/debug_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ void FWrite(FILE* file, const std::string& str);
V(INSPECTOR_SERVER) \
V(INSPECTOR_PROFILER) \
V(CODE_CACHE) \
V(WASI)
V(WASI) \
V(MKSNAPSHOT)

enum class DebugCategory {
#define V(name) name,
Expand Down
43 changes: 10 additions & 33 deletions src/env-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,29 +77,6 @@ inline v8::Local<v8::String> 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_;
}
Expand Down Expand Up @@ -238,9 +215,6 @@ inline void Environment::PopAsyncCallbackScope() {
async_callback_scope_depth_--;
}

inline ImmediateInfo::ImmediateInfo(v8::Isolate* isolate)
: fields_(isolate, kFieldsCount) {}

inline AliasedUint32Array& ImmediateInfo::fields() {
return fields_;
}
Expand All @@ -265,9 +239,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_;
}
Expand Down Expand Up @@ -365,16 +336,22 @@ inline v8::Local<v8::Uint32> BindingDataBase::New(
Environment* env,
v8::Local<v8::Context> context,
v8::Local<v8::Object> target) {
T* data = new T(env, target);
return Initialize(context, new T(env, target));
}

template <typename T>
inline v8::Local<v8::Uint32> BindingDataBase::Initialize(
v8::Local<v8::Context> context, T* binding) {
// This won't compile if T is not a BindingDataBase subclass.
BindingDataBase* item = static_cast<BindingDataBase*>(data);
BindingDataBase* item = static_cast<BindingDataBase*>(binding);
std::vector<BindingDataBase*>* list =
static_cast<std::vector<BindingDataBase*>*>(
context->GetAlignedPointerFromEmbedderData(
ContextEmbedderIndex::KBindingListIndex));
size_t index = list->size();
list->push_back(item);
return v8::Integer::NewFromUnsigned(env->isolate(), index).As<v8::Uint32>();
return v8::Integer::NewFromUnsigned(context->GetIsolate(), index)
.As<v8::Uint32>();
}

template <typename T>
Expand All @@ -384,7 +361,7 @@ Environment::BindingScope<T>::BindingScope(Environment* env,
: env(env) {
v8::Local<v8::Uint32> index = BindingDataBase::New<T>(env, context, target);
data = BindingDataBase::Unwrap<T>(context, index);
env->set_current_callback_data(index);
env->set_current_callback_data(index.As<v8::Uint32>());
}

template <typename T>
Expand Down
Loading

0 comments on commit 7d0932e

Please sign in to comment.