Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: define per-isolate internal bindings registration callback #45547

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/async_wrap-inl.h
Expand Up @@ -80,6 +80,12 @@ inline v8::MaybeLocal<v8::Value> AsyncWrap::MakeCallback(
return MakeCallback(cb_v.As<v8::Function>(), argc, argv);
}

// static
inline v8::Local<v8::FunctionTemplate> AsyncWrap::GetConstructorTemplate(
Environment* env) {
return GetConstructorTemplate(env->isolate_data());
}

} // namespace node

#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
Expand Down
14 changes: 8 additions & 6 deletions src/async_wrap.cc
Expand Up @@ -334,18 +334,20 @@ void AsyncWrap::SetCallbackTrampoline(const FunctionCallbackInfo<Value>& args) {
}
}

Local<FunctionTemplate> AsyncWrap::GetConstructorTemplate(Environment* env) {
Local<FunctionTemplate> tmpl = env->async_wrap_ctor_template();
Local<FunctionTemplate> AsyncWrap::GetConstructorTemplate(
IsolateData* isolate_data) {
Local<FunctionTemplate> tmpl = isolate_data->async_wrap_ctor_template();
if (tmpl.IsEmpty()) {
Isolate* isolate = env->isolate();
Isolate* isolate = isolate_data->isolate();
tmpl = NewFunctionTemplate(isolate, nullptr);
tmpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "AsyncWrap"));
tmpl->Inherit(BaseObject::GetConstructorTemplate(env));
tmpl->SetClassName(
FIXED_ONE_BYTE_STRING(isolate_data->isolate(), "AsyncWrap"));
tmpl->Inherit(BaseObject::GetConstructorTemplate(isolate_data));
SetProtoMethod(isolate, tmpl, "getAsyncId", AsyncWrap::GetAsyncId);
SetProtoMethod(isolate, tmpl, "asyncReset", AsyncWrap::AsyncReset);
SetProtoMethod(
isolate, tmpl, "getProviderType", AsyncWrap::GetProviderType);
env->set_async_wrap_ctor_template(tmpl);
isolate_data->set_async_wrap_ctor_template(tmpl);
}
return tmpl;
}
Expand Down
2 changes: 2 additions & 0 deletions src/async_wrap.h
Expand Up @@ -138,6 +138,8 @@ class AsyncWrap : public BaseObject {
static constexpr double kInvalidAsyncId = -1;

static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
IsolateData* isolate_data);
inline static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
Environment* env);

static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
Expand Down
3 changes: 3 additions & 0 deletions src/env-inl.h
Expand Up @@ -827,6 +827,7 @@ void Environment::set_process_exit_handler(
#undef VY
#undef VP

#define VM(PropertyName) V(PropertyName##_binding, v8::FunctionTemplate)
#define V(PropertyName, TypeName) \
inline v8::Local<TypeName> IsolateData::PropertyName() const { \
return PropertyName##_.Get(isolate_); \
Expand All @@ -835,7 +836,9 @@ void Environment::set_process_exit_handler(
PropertyName##_.Set(isolate_, value); \
}
PER_ISOLATE_TEMPLATE_PROPERTIES(V)
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM)
#undef V
#undef VM

#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
#define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName)
Expand Down
26 changes: 5 additions & 21 deletions src/env.cc
Expand Up @@ -314,6 +314,7 @@ IsolateDataSerializeInfo IsolateData::Serialize(SnapshotCreator* creator) {
info.primitive_values.push_back(creator->AddData(async_wrap_provider(i)));

uint32_t id = 0;
#define VM(PropertyName) V(PropertyName##_binding, FunctionTemplate)
#define V(PropertyName, TypeName) \
do { \
Local<TypeName> field = PropertyName(); \
Expand All @@ -324,6 +325,7 @@ IsolateDataSerializeInfo IsolateData::Serialize(SnapshotCreator* creator) {
id++; \
} while (0);
PER_ISOLATE_TEMPLATE_PROPERTIES(V)
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM)
#undef V

return info;
Expand Down Expand Up @@ -368,6 +370,7 @@ void IsolateData::DeserializeProperties(const IsolateDataSerializeInfo* info) {
const std::vector<PropInfo>& values = info->template_values;
i = 0; // index to the array
uint32_t id = 0;
#define VM(PropertyName) V(PropertyName##_binding, FunctionTemplate)
#define V(PropertyName, TypeName) \
do { \
if (values.size() > i && id == values[i].id) { \
Expand All @@ -388,6 +391,7 @@ void IsolateData::DeserializeProperties(const IsolateDataSerializeInfo* info) {
} while (0);

PER_ISOLATE_TEMPLATE_PROPERTIES(V);
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM);
#undef V
}

Expand Down Expand Up @@ -454,12 +458,12 @@ void IsolateData::CreateProperties() {
NODE_ASYNC_PROVIDER_TYPES(V)
#undef V

// TODO(legendecas): eagerly create per isolate templates.
Local<FunctionTemplate> templ = FunctionTemplate::New(isolate());
templ->InstanceTemplate()->SetInternalFieldCount(
BaseObject::kInternalFieldCount);
templ->Inherit(BaseObject::GetConstructorTemplate(this));
set_binding_data_ctor_template(templ);
binding::CreateInternalBindingTemplates(this);

contextify::ContextifyContext::InitializeGlobalTemplates(this);
}
Expand Down Expand Up @@ -1581,30 +1585,13 @@ void Environment::PrintInfoForSnapshotIfDebug() {
if (enabled_debug_list()->enabled(DebugCategory::MKSNAPSHOT)) {
fprintf(stderr, "At the exit of the Environment:\n");
principal_realm()->PrintInfoForSnapshot();
fprintf(stderr, "\nBuiltins without cache:\n");
for (const auto& s : builtins_without_cache) {
fprintf(stderr, "%s\n", s.c_str());
}
fprintf(stderr, "\nBuiltins with cache:\n");
for (const auto& s : builtins_with_cache) {
fprintf(stderr, "%s\n", s.c_str());
}
fprintf(stderr, "\nStatic bindings (need to be registered):\n");
for (const auto mod : internal_bindings) {
fprintf(stderr, "%s:%s\n", mod->nm_filename, mod->nm_modname);
}
}
}

EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
EnvSerializeInfo info;
Local<Context> ctx = context();

// Currently all modules are compiled without cache in builtin snapshot
// builder.
info.builtins = std::vector<std::string>(builtins_without_cache.begin(),
builtins_without_cache.end());

info.async_hooks = async_hooks_.Serialize(ctx, creator);
info.immediate_info = immediate_info_.Serialize(ctx, creator);
info.tick_info = tick_info_.Serialize(ctx, creator);
Expand Down Expand Up @@ -1650,7 +1637,6 @@ void Environment::DeserializeProperties(const EnvSerializeInfo* info) {

RunDeserializeRequests();

builtins_in_snapshot = info->builtins;
async_hooks_.Deserialize(ctx);
immediate_info_.Deserialize(ctx);
tick_info_.Deserialize(ctx);
Expand Down Expand Up @@ -1841,8 +1827,6 @@ void Environment::MemoryInfo(MemoryTracker* tracker) const {
// Iteratable STLs have their own sizes subtracted from the parent
// by default.
tracker->TrackField("isolate_data", isolate_data_);
tracker->TrackField("builtins_with_cache", builtins_with_cache);
tracker->TrackField("builtins_without_cache", builtins_without_cache);
tracker->TrackField("destroy_async_id_list", destroy_async_id_list_);
tracker->TrackField("exec_argv", exec_argv_);
tracker->TrackField("exit_info", exit_info_);
Expand Down
15 changes: 6 additions & 9 deletions src/env.h
Expand Up @@ -155,11 +155,14 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer {
#undef VS
#undef VP

#define VM(PropertyName) V(PropertyName##_binding, v8::FunctionTemplate)
#define V(PropertyName, TypeName) \
inline v8::Local<TypeName> PropertyName() const; \
inline void set_##PropertyName(v8::Local<TypeName> value);
PER_ISOLATE_TEMPLATE_PROPERTIES(V)
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM)
#undef V
#undef VM

inline v8::Local<v8::String> async_wrap_provider(int index) const;

Expand All @@ -179,15 +182,17 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer {
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
#define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName)
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
#define VM(PropertyName) V(v8::FunctionTemplate, PropertyName##_binding)
#define VT(PropertyName, TypeName) V(TypeName, PropertyName)
#define V(TypeName, PropertyName) \
v8::Eternal<TypeName> PropertyName ## _;
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
PER_ISOLATE_SYMBOL_PROPERTIES(VY)
PER_ISOLATE_STRING_PROPERTIES(VS)
PER_ISOLATE_TEMPLATE_PROPERTIES(VT)
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM)
#undef V
#undef V
#undef VM
#undef VT
#undef VS
#undef VY
Expand Down Expand Up @@ -457,7 +462,6 @@ struct DeserializeRequest {
};

struct EnvSerializeInfo {
std::vector<std::string> builtins;
AsyncHooks::SerializeInfo async_hooks;
TickInfo::SerializeInfo tick_info;
ImmediateInfo::SerializeInfo immediate_info;
Expand Down Expand Up @@ -715,13 +719,6 @@ class Environment : public MemoryRetainer {
// List of id's that have been destroyed and need the destroy() cb called.
inline std::vector<double>* destroy_async_id_list();

std::set<struct node_module*> internal_bindings;
std::set<std::string> builtins_with_cache;
std::set<std::string> builtins_without_cache;
// This is only filled during deserialization. We use a vector since
// it's only used for tests.
std::vector<std::string> builtins_in_snapshot;

std::unordered_multimap<int, loader::ModuleWrap*> hash_to_module_map;
std::unordered_map<uint32_t, loader::ModuleWrap*> id_to_module_map;
std::unordered_map<uint32_t, contextify::ContextifyScript*>
Expand Down
1 change: 1 addition & 0 deletions src/inspector_js_api.cc
@@ -1,3 +1,4 @@
#include "async_wrap-inl.h"
#include "base_object-inl.h"
#include "inspector_agent.h"
#include "inspector_io.h"
Expand Down
95 changes: 70 additions & 25 deletions src/node_binding.cc
Expand Up @@ -102,6 +102,12 @@
NODE_BUILTIN_BINDINGS(V)
#undef V

#define V(modname) \
void _register_isolate_##modname(node::IsolateData* isolate_data, \
v8::Local<v8::FunctionTemplate> target);
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V)
#undef V

#ifdef _AIX
// On AIX, dlopen() behaves differently from other operating systems, in that
// it returns unique values from each call, rather than identical values, when
Expand Down Expand Up @@ -229,9 +235,12 @@ static bool libc_may_be_musl() { return false; }
namespace node {

using v8::Context;
using v8::EscapableHandleScope;
using v8::Exception;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
Expand Down Expand Up @@ -552,50 +561,86 @@ inline struct node_module* FindModule(struct node_module* list,
return mp;
}

static Local<Object> InitInternalBinding(Environment* env,
node_module* mod,
Local<String> module) {
// Internal bindings don't have a "module" object, only exports.
Local<Function> ctor = env->binding_data_ctor_template()
->GetFunction(env->context())
.ToLocalChecked();
Local<Object> exports = ctor->NewInstance(env->context()).ToLocalChecked();
void CreateInternalBindingTemplates(IsolateData* isolate_data) {
#define V(modname) \
do { \
Local<FunctionTemplate> templ = \
FunctionTemplate::New(isolate_data->isolate()); \
templ->InstanceTemplate()->SetInternalFieldCount( \
BaseObject::kInternalFieldCount); \
templ->Inherit(BaseObject::GetConstructorTemplate(isolate_data)); \
_register_isolate_##modname(isolate_data, templ); \
isolate_data->set_##modname##_binding(templ); \
} while (0);
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V)
#undef V
}

static Local<Object> GetInternalBindingExportObject(IsolateData* isolate_data,
const char* mod_name,
Local<Context> context) {
Local<FunctionTemplate> ctor;
#define V(name) \
if (strcmp(mod_name, #name) == 0) { \
ctor = isolate_data->name##_binding(); \
} else // NOLINT(readability/braces)
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V)
cjihrig marked this conversation as resolved.
Show resolved Hide resolved
#undef V
{
ctor = isolate_data->binding_data_ctor_template();
}

Local<Object> obj = ctor->GetFunction(context)
.ToLocalChecked()
->NewInstance(context)
.ToLocalChecked();
return obj;
}

static Local<Object> InitInternalBinding(Realm* realm, node_module* mod) {
EscapableHandleScope scope(realm->isolate());
Local<Context> context = realm->context();
Local<Object> exports = GetInternalBindingExportObject(
realm->isolate_data(), mod->nm_modname, context);
CHECK_NULL(mod->nm_register_func);
CHECK_NOT_NULL(mod->nm_context_register_func);
Local<Value> unused = Undefined(env->isolate());
mod->nm_context_register_func(exports, unused, env->context(), mod->nm_priv);
return exports;
Local<Value> unused = Undefined(realm->isolate());
// Internal bindings don't have a "module" object, only exports.
mod->nm_context_register_func(exports, unused, context, mod->nm_priv);
return scope.Escape(exports);
}

void GetInternalBinding(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Realm* realm = Realm::GetCurrent(args);
Isolate* isolate = realm->isolate();
HandleScope scope(isolate);
Local<Context> context = realm->context();

CHECK(args[0]->IsString());

Local<String> module = args[0].As<String>();
node::Utf8Value module_v(env->isolate(), module);
node::Utf8Value module_v(isolate, module);
Local<Object> exports;

node_module* mod = FindModule(modlist_internal, *module_v, NM_F_INTERNAL);
if (mod != nullptr) {
exports = InitInternalBinding(env, mod, module);
env->internal_bindings.insert(mod);
exports = InitInternalBinding(realm, mod);
realm->internal_bindings.insert(mod);
} else if (!strcmp(*module_v, "constants")) {
exports = Object::New(env->isolate());
CHECK(
exports->SetPrototype(env->context(), Null(env->isolate())).FromJust());
DefineConstants(env->isolate(), exports);
exports = Object::New(isolate);
CHECK(exports->SetPrototype(context, Null(isolate)).FromJust());
DefineConstants(isolate, exports);
} else if (!strcmp(*module_v, "natives")) {
exports = builtins::BuiltinLoader::GetSourceObject(env->context());
exports = builtins::BuiltinLoader::GetSourceObject(context);
// Legacy feature: process.binding('natives').config contains stringified
// config.gypi
CHECK(exports
->Set(env->context(),
env->config_string(),
builtins::BuiltinLoader::GetConfigString(env->isolate()))
->Set(context,
realm->isolate_data()->config_string(),
builtins::BuiltinLoader::GetConfigString(isolate))
.FromJust());
} else {
return THROW_ERR_INVALID_MODULE(env, "No such binding: %s", *module_v);
return THROW_ERR_INVALID_MODULE(isolate, "No such binding: %s", *module_v);
}

args.GetReturnValue().Set(exports);
Expand Down