Skip to content

Commit

Permalink
fixup! src: use an array for faster binding data lookup
Browse files Browse the repository at this point in the history
  • Loading branch information
joyeecheung committed Feb 13, 2023
1 parent c841b77 commit 8a46ff7
Show file tree
Hide file tree
Showing 14 changed files with 97 additions and 47 deletions.
1 change: 1 addition & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,7 @@
'src/async_wrap-inl.h',
'src/base_object.h',
'src/base_object-inl.h',
'src/base_object_types.h',
'src/base64.h',
'src/base64-inl.h',
'src/callback_queue.h',
Expand Down
28 changes: 16 additions & 12 deletions src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -486,28 +486,32 @@ that state is through the use of `Environment::AddBindingData`, which gives
binding functions access to an object for storing such state.
That object is always a [`BaseObject`][].

If the binding should be supported in a snapshot, it needs to be in the
`SERIALIZABLE_OBJECT_TYPES` list in `node_snapshotable.h` and implement the
serialization and deserialization methods. See the comments of
`SnapshotableObject` on how to implement them. Otherwise, the `type_int` field
needs to be added to the `UNSERIALIZABLE_OBJECT_TYPES` list.
In the binding, call `SET_BINDING_ID()` with an identifier for the binding
type. For example, for `http_parser::BindingData`, the identifier can be
`http_parser_binding_data`.

If the binding should be supported in a snapshot, the id and the
fully-specified class name should be added to the `SERIALIZABLE_BINDING_TYPES`
list in `base_object_types.h`, and the class should implement the serialization
and deserialization methods. See the comments of `SnapshotableObject` on how to
implement them. Otherwise, add the id and the class name to the
`UNSERIALIZABLE_BINDING_TYPES` list instead.

```cpp
// In node_snapshotable.h, add the binding to either UNSERIALIZABLE_OBJECT_TYPES
// or SERIALIZABLE_OBJECT_TYPES. The second parameter is a descriptive name
// of the class, which is usually the class name with the (actual or conceptual)
// namespace.
// In base_object_types.h, add the binding to either
// UNSERIALIZABLE_BINDING_TYPES or SERIALIZABLE_BINDING_TYPES.
// The second parameter is a descriptive name of the class, which is
// usually the fully-specified class name.

#define UNSERIALIZABLE_OBJECT_TYPES(V) \
#define UNSERIALIZABLE_BINDING_TYPES(V) \
V(http_parser_binding_data, http_parser::BindingData)

// In the HTTP parser source code file:
class BindingData : public BaseObject {
public:
BindingData(Environment* env, Local<Object> obj) : BaseObject(env, obj) {}

static constexpr EmbedderObjectType type_int =
EmbedderObjectType::k_http_parser_binding_data;
SET_BINDING_ID(http_parser_binding_data)

std::vector<char> parser_buffer;
bool parser_buffer_in_use = false;
Expand Down
1 change: 1 addition & 0 deletions src/base_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include <type_traits> // std::remove_reference
#include "base_object_types.h"
#include "memory_tracker.h"
#include "v8.h"

Expand Down
69 changes: 69 additions & 0 deletions src/base_object_types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#ifndef SRC_BASE_OBJECT_TYPES_H_
#define SRC_BASE_OBJECT_TYPES_H_

#include <cinttypes>

#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

namespace node {
// List of internalBinding() data wrappers. The first argument should match
// what the class passes to SET_BINDING_ID(), the second argument should match
// the C++ class name.
#define SERIALIZABLE_BINDING_TYPES(V) \
V(fs_binding_data, fs::BindingData) \
V(v8_binding_data, v8_utils::BindingData) \
V(blob_binding_data, BlobBindingData) \
V(process_binding_data, process::BindingData)

#define UNSERIALIZABLE_BINDING_TYPES(V) \
V(http2_binding_data, http2::BindingData) \
V(http_parser_binding_data, http_parser::BindingData)

// List of (non-binding) BaseObjects that are serializable in the snapshot.
// The first argument should match what the type passes to
// SET_OBJECT_ID(), the second argument should match the C++ class
// name.
#define SERIALIZABLE_NON_BINDING_TYPES(V) \
V(util_weak_reference, util::WeakReference)

// Helper list of all binding data wrapper types.
#define BINDING_TYPES(V) \
SERIALIZABLE_BINDING_TYPES(V) \
UNSERIALIZABLE_BINDING_TYPES(V)

// Helper list of all BaseObjects that implement snapshot support.
#define SERIALIZABLE_OBJECT_TYPES(V) \
SERIALIZABLE_BINDING_TYPES(V) \
SERIALIZABLE_NON_BINDING_TYPES(V)

#define V(TypeId, NativeType) k_##TypeId,
enum class BindingDataType : uint8_t { BINDING_TYPES(V) kBindingDataTypeCount };
// Make sure that we put the bindings first so that we can also use the enums
// for the bindings as index to the binding data store.
enum class EmbedderObjectType : uint8_t {
BINDING_TYPES(V) SERIALIZABLE_NON_BINDING_TYPES(V)
// We do not need to know about all the unserializable non-binding types for
// now so we do not list them.
kEmbedderObjectTypeCount
};
#undef V

// For now, BaseObjects only need to call this when they implement snapshot
// support.
#define SET_OBJECT_ID(TypeId) \
static constexpr EmbedderObjectType type_int = EmbedderObjectType::k_##TypeId;

// Binding data should call this so that they can be looked up from the binding
// data store.
#define SET_BINDING_ID(TypeId) \
static constexpr BindingDataType binding_type_int = \
BindingDataType::k_##TypeId; \
SET_OBJECT_ID(TypeId) \
static_assert(static_cast<uint8_t>(type_int) == \
static_cast<uint8_t>(binding_type_int));

} // namespace node

#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#endif // SRC_BASE_OBJECT_TYPES_H_
4 changes: 2 additions & 2 deletions src/env-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ inline T* Environment::GetBindingData(v8::Local<v8::Context> context) {
context->GetAlignedPointerFromEmbedderData(
ContextEmbedderIndex::kBindingListIndex));
DCHECK_NOT_NULL(map);
constexpr size_t binding_index = static_cast<size_t>(T::type_int);
constexpr size_t binding_index = static_cast<size_t>(T::binding_type_int);
static_assert(binding_index < std::tuple_size_v<BindingDataStore>);
auto ptr = (*map)[binding_index];
if (UNLIKELY(!ptr)) return nullptr;
Expand All @@ -239,7 +239,7 @@ inline T* Environment::AddBindingData(
context->GetAlignedPointerFromEmbedderData(
ContextEmbedderIndex::kBindingListIndex));
DCHECK_NOT_NULL(map);
constexpr size_t binding_index = static_cast<size_t>(T::type_int);
constexpr size_t binding_index = static_cast<size_t>(T::binding_type_int);
static_assert(binding_index < std::tuple_size_v<BindingDataStore>);
CHECK(!(*map)[binding_index]); // Should not insert the binding twice.
(*map)[binding_index] = item;
Expand Down
2 changes: 1 addition & 1 deletion src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ class Environment : public MemoryRetainer {

typedef std::array<BaseObjectPtr<BaseObject>,
static_cast<size_t>(
EmbedderObjectType::kEmbedderObjectTypeCount)>
BindingDataType::kBindingDataTypeCount)>
BindingDataStore;

// Create an Environment without initializing a main Context. Use
Expand Down
3 changes: 1 addition & 2 deletions src/node_blob.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,7 @@ class BlobBindingData : public SnapshotableObject {

SERIALIZABLE_OBJECT_METHODS()

static constexpr EmbedderObjectType type_int =
EmbedderObjectType::k_blob_binding_data;
SET_BINDING_ID(blob_binding_data)

void MemoryInfo(MemoryTracker* tracker) const override;
SET_SELF_SIZE(BlobBindingData)
Expand Down
3 changes: 1 addition & 2 deletions src/node_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ class BindingData : public SnapshotableObject {

using InternalFieldInfo = InternalFieldInfoBase;
SERIALIZABLE_OBJECT_METHODS()
static constexpr EmbedderObjectType type_int =
EmbedderObjectType::k_fs_binding_data;
SET_BINDING_ID(fs_binding_data)

void MemoryInfo(MemoryTracker* tracker) const override;
SET_SELF_SIZE(BindingData)
Expand Down
3 changes: 1 addition & 2 deletions src/node_http2_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,7 @@ class Http2State : public BaseObject {
SET_SELF_SIZE(Http2State)
SET_MEMORY_INFO_NAME(Http2State)

static constexpr EmbedderObjectType type_int =
EmbedderObjectType::k_http2_binding_data;
SET_BINDING_ID(http2_binding_data)

private:
struct http2_state_internal {
Expand Down
3 changes: 1 addition & 2 deletions src/node_http_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ class BindingData : public BaseObject {
BindingData(Environment* env, Local<Object> obj)
: BaseObject(env, obj) {}

static constexpr EmbedderObjectType type_int =
EmbedderObjectType::k_http_parser_binding_data;
SET_BINDING_ID(http_parser_binding_data)

std::vector<char> parser_buffer;
bool parser_buffer_in_use = false;
Expand Down
3 changes: 1 addition & 2 deletions src/node_process.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ class BindingData : public SnapshotableObject {
using InternalFieldInfo = InternalFieldInfoBase;

SERIALIZABLE_OBJECT_METHODS()
static constexpr EmbedderObjectType type_int =
EmbedderObjectType::k_process_binding_data;
SET_BINDING_ID(process_binding_data)

BindingData(Environment* env, v8::Local<v8::Object> object);

Expand Down
18 changes: 0 additions & 18 deletions src/node_snapshotable.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,6 @@ struct PropInfo {
SnapshotIndex index; // In the snapshot
};

#define UNSERIALIZABLE_OBJECT_TYPES(V) \
V(http2_binding_data, http2::BindingData) \
V(http_parser_binding_data, http_parser::BindingData)

#define SERIALIZABLE_OBJECT_TYPES(V) \
V(fs_binding_data, fs::BindingData) \
V(v8_binding_data, v8_utils::BindingData) \
V(blob_binding_data, BlobBindingData) \
V(process_binding_data, process::BindingData) \
V(util_weak_reference, util::WeakReference)

enum class EmbedderObjectType : uint8_t {
#define V(PropertyName, NativeType) k_##PropertyName,
SERIALIZABLE_OBJECT_TYPES(V) UNSERIALIZABLE_OBJECT_TYPES(V)
#undef V
kEmbedderObjectTypeCount
};

typedef size_t SnapshotIndex;

// When serializing an embedder object, we'll serialize the native states
Expand Down
3 changes: 1 addition & 2 deletions src/node_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ class WeakReference : public SnapshotableObject {
public:
SERIALIZABLE_OBJECT_METHODS()

static constexpr EmbedderObjectType type_int =
EmbedderObjectType::k_util_weak_reference;
SET_OBJECT_ID(util_weak_reference)

WeakReference(Environment* env,
v8::Local<v8::Object> object,
Expand Down
3 changes: 1 addition & 2 deletions src/node_v8.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ class BindingData : public SnapshotableObject {
using InternalFieldInfo = InternalFieldInfoBase;

SERIALIZABLE_OBJECT_METHODS()
static constexpr EmbedderObjectType type_int =
EmbedderObjectType::k_v8_binding_data;
SET_BINDING_ID(v8_binding_data)

AliasedFloat64Array heap_statistics_buffer;
AliasedFloat64Array heap_space_statistics_buffer;
Expand Down

0 comments on commit 8a46ff7

Please sign in to comment.