Skip to content

Commit

Permalink
src: add node_modules.h for module loader
Browse files Browse the repository at this point in the history
  • Loading branch information
anonrig committed Oct 22, 2023
1 parent cd6b86b commit ba0be9b
Show file tree
Hide file tree
Showing 14 changed files with 241 additions and 76 deletions.
12 changes: 2 additions & 10 deletions lib/internal/modules/package_json_reader.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
const {
JSONParse,
ObjectPrototypeHasOwnProperty,
SafeMap,
StringPrototypeEndsWith,
StringPrototypeIndexOf,
StringPrototypeLastIndexOf,
Expand All @@ -12,15 +11,13 @@ const {
const {
ERR_INVALID_PACKAGE_CONFIG,
} = require('internal/errors').codes;
const { internalModuleReadJSON } = internalBinding('fs');
const modulesBinding = internalBinding('modules');
const { resolve, sep, toNamespacedPath } = require('path');
const permission = require('internal/process/permission');
const { kEmptyObject, setOwnProperty } = require('internal/util');

const { fileURLToPath, pathToFileURL } = require('internal/url');

const cache = new SafeMap();

let manifest;

/**
Expand All @@ -45,11 +42,7 @@ let manifest;
* @returns {PackageConfig}
*/
function read(jsonPath, { base, specifier, isESM } = kEmptyObject) {
if (cache.has(jsonPath)) {
return cache.get(jsonPath);
}

const string = internalModuleReadJSON(
const string = modulesBinding.readPackageJSON(
toNamespacedPath(jsonPath),
);
const result = {
Expand Down Expand Up @@ -112,7 +105,6 @@ function read(jsonPath, { base, specifier, isESM } = kEmptyObject) {
manifest.assertIntegrity(jsonURL, string);
}
}
cache.set(jsonPath, result);
return result;
}

Expand Down
2 changes: 2 additions & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
'src/node_main_instance.cc',
'src/node_messaging.cc',
'src/node_metadata.cc',
'src/node_modules.cc',
'src/node_options.cc',
'src/node_os.cc',
'src/node_perf.cc',
Expand Down Expand Up @@ -234,6 +235,7 @@
'src/node_messaging.h',
'src/node_metadata.h',
'src/node_mutex.h',
'src/node_modules.h',
'src/node_object_wrap.h',
'src/node_options.h',
'src/node_options-inl.h',
Expand Down
3 changes: 2 additions & 1 deletion src/base_object_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ namespace node {
V(blob_binding_data, BlobBindingData) \
V(process_binding_data, process::BindingData) \
V(timers_binding_data, timers::BindingData) \
V(url_binding_data, url::BindingData)
V(url_binding_data, url::BindingData) \
V(modules_binding_data, modules::BindingData)

#define UNSERIALIZABLE_BINDING_TYPES(V) \
V(http2_binding_data, http2::BindingData) \
Expand Down
1 change: 1 addition & 0 deletions src/node_binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
V(js_stream) \
V(js_udp_wrap) \
V(messaging) \
V(modules) \
V(module_wrap) \
V(mksnapshot) \
V(options) \
Expand Down
1 change: 1 addition & 0 deletions src/node_binding.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ static_assert(static_cast<int>(NM_F_LINKED) ==
V(encoding_binding) \
V(fs) \
V(mksnapshot) \
V(modules) \
V(timers) \
V(process_methods) \
V(performance) \
Expand Down
1 change: 1 addition & 0 deletions src/node_external_reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ class ExternalReferenceRegistry {
V(messaging) \
V(mksnapshot) \
V(module_wrap) \
V(modules) \
V(options) \
V(os) \
V(performance) \
Expand Down
64 changes: 0 additions & 64 deletions src/node_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1050,68 +1050,6 @@ static void ExistsSync(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(err == 0);
}

// Used to speed up module loading. Returns an array [string, boolean]
static void InternalModuleReadJSON(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
uv_loop_t* loop = env->event_loop();

CHECK(args[0]->IsString());
node::Utf8Value path(isolate, args[0]);
THROW_IF_INSUFFICIENT_PERMISSIONS(
env, permission::PermissionScope::kFileSystemRead, path.ToStringView());

if (strlen(*path) != path.length()) {
return; // Contains a nul byte.
}
uv_fs_t open_req;
const int fd = uv_fs_open(loop, &open_req, *path, O_RDONLY, 0, nullptr);
uv_fs_req_cleanup(&open_req);

if (fd < 0) {
return;
}

auto defer_close = OnScopeLeave([fd, loop]() {
uv_fs_t close_req;
CHECK_EQ(0, uv_fs_close(loop, &close_req, fd, nullptr));
uv_fs_req_cleanup(&close_req);
});

const size_t kBlockSize = 32 << 10;
std::vector<char> chars;
int64_t offset = 0;
ssize_t numchars;
do {
const size_t start = chars.size();
chars.resize(start + kBlockSize);

uv_buf_t buf;
buf.base = &chars[start];
buf.len = kBlockSize;

uv_fs_t read_req;
numchars = uv_fs_read(loop, &read_req, fd, &buf, 1, offset, nullptr);
uv_fs_req_cleanup(&read_req);

if (numchars < 0) {
return;
}
offset += numchars;
} while (static_cast<size_t>(numchars) == kBlockSize);

size_t start = 0;
if (offset >= 3 && 0 == memcmp(chars.data(), "\xEF\xBB\xBF", 3)) {
start = 3; // Skip UTF-8 BOM.
}
const size_t size = offset - start;

args.GetReturnValue().Set(
String::NewFromUtf8(
isolate, &chars[start], v8::NewStringType::kNormal, size)
.ToLocalChecked());
}

// Used to speed up module loading. Returns 0 if the path refers to
// a file, 1 when it's a directory or < 0 on error (usually -ENOENT.)
// The speedup comes from not creating thousands of Stat and Error objects.
Expand Down Expand Up @@ -3255,7 +3193,6 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
SetMethod(isolate, target, "rmdir", RMDir);
SetMethod(isolate, target, "mkdir", MKDir);
SetMethod(isolate, target, "readdir", ReadDir);
SetMethod(isolate, target, "internalModuleReadJSON", InternalModuleReadJSON);
SetMethod(isolate, target, "internalModuleStat", InternalModuleStat);
SetMethod(isolate, target, "stat", Stat);
SetMethod(isolate, target, "lstat", LStat);
Expand Down Expand Up @@ -3375,7 +3312,6 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(RMDir);
registry->Register(MKDir);
registry->Register(ReadDir);
registry->Register(InternalModuleReadJSON);
registry->Register(InternalModuleStat);
registry->Register(Stat);
registry->Register(LStat);
Expand Down
173 changes: 173 additions & 0 deletions src/node_modules.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#include "node_modules.h"
#include "base_object-inl.h"
#include "node_errors.h"
#include "node_external_reference.h"
#include "util-inl.h"
#include "v8-fast-api-calls.h"
#include "v8.h"

namespace node {
namespace modules {

using v8::CFunction;
using v8::Context;
using v8::FastOneByteString;
using v8::FunctionCallbackInfo;
using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::MaybeLocal;
using v8::NewStringType;
using v8::Object;
using v8::ObjectTemplate;
using v8::String;
using v8::Value;

void BindingData::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackField("package_json_files", package_json_files_);
}

BindingData::BindingData(Realm* realm, v8::Local<v8::Object> object)
: SnapshotableObject(realm, object, type_int) {
package_json_files_ =
v8::Global<v8::Map>(realm->isolate(), v8::Map::New(realm->isolate()));
object
->Set(realm->context(),
FIXED_ONE_BYTE_STRING(realm->isolate(), "packageJsonFiles"),
package_json_files_.Get(realm->isolate()))
.Check();

package_json_files_.SetWeak();
}

bool BindingData::PrepareForSerialization(v8::Local<v8::Context> context,
v8::SnapshotCreator* creator) {
package_json_files_.Reset();
// Return true because we need to maintain the reference to the binding from
// JS land.
return true;
}

InternalFieldInfoBase* BindingData::Serialize(int index) {
DCHECK_IS_SNAPSHOT_SLOT(index);
InternalFieldInfo* info =
InternalFieldInfoBase::New<InternalFieldInfo>(type());
return info;
}

void BindingData::Deserialize(v8::Local<v8::Context> context,
v8::Local<v8::Object> holder,
int index,
InternalFieldInfoBase* info) {
DCHECK_IS_SNAPSHOT_SLOT(index);
v8::HandleScope scope(context->GetIsolate());
Realm* realm = Realm::GetCurrent(context);
BindingData* binding = realm->AddBindingData<BindingData>(holder);
CHECK_NOT_NULL(binding);
}

void BindingData::ReadPackageJSON(const FunctionCallbackInfo<Value>& args) {
CHECK_GE(args.Length(), 1);
CHECK(args[0]->IsString());

Realm* realm = Realm::GetCurrent(args);
auto isolate = realm->isolate();
auto env = realm->env();
auto context = realm->context();
auto binding_data = realm->GetBindingData<BindingData>();
auto package_json_files_ = binding_data->package_json_files_.Get(isolate);

Utf8Value path(isolate, args[0]);
THROW_IF_INSUFFICIENT_PERMISSIONS(
env, permission::PermissionScope::kFileSystemRead, path.ToStringView());

if (strlen(*path) != path.length()) {
return; // Contains a nul byte.
}

Local<Value> cache_key =
ToV8Value(context, path.ToStringView(), isolate).ToLocalChecked();
MaybeLocal<Value> maybe_existing =
package_json_files_->Get(context, cache_key);
Local<Value> existing_value;
if (maybe_existing.ToLocal(&existing_value) && existing_value->IsString()) {
return args.GetReturnValue().Set(existing_value);
}

uv_fs_t open_req;
const int fd = uv_fs_open(nullptr, &open_req, *path, O_RDONLY, 0, nullptr);
uv_fs_req_cleanup(&open_req);

if (fd < 0) {
return;
}

auto defer_close = OnScopeLeave([fd]() {
uv_fs_t close_req;
CHECK_EQ(0, uv_fs_close(nullptr, &close_req, fd, nullptr));
uv_fs_req_cleanup(&close_req);
});

const size_t kBlockSize = 32 << 10;
std::vector<char> chars;
int64_t offset = 0;
ssize_t numchars;
do {
const size_t start = chars.size();
chars.resize(start + kBlockSize);

uv_buf_t buf;
buf.base = &chars[start];
buf.len = kBlockSize;

uv_fs_t read_req;
numchars = uv_fs_read(nullptr, &read_req, fd, &buf, 1, offset, nullptr);
uv_fs_req_cleanup(&read_req);

if (numchars < 0) {
return;
}
offset += numchars;
} while (static_cast<size_t>(numchars) == kBlockSize);

size_t start = 0;
if (offset >= 3 && 0 == memcmp(chars.data(), "\xEF\xBB\xBF", 3)) {
start = 3; // Skip UTF-8 BOM.
}

auto content =
ToV8Value(
context, std::string_view(&chars[start], offset - start), isolate)
.ToLocalChecked();
USE(package_json_files_->Set(context, cache_key, content));
args.GetReturnValue().Set(content);
}

void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
Local<ObjectTemplate> target) {
Isolate* isolate = isolate_data->isolate();
SetMethod(isolate, target, "readPackageJSON", ReadPackageJSON);
}

void BindingData::CreatePerContextProperties(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
Realm* realm = Realm::GetCurrent(context);
realm->AddBindingData<BindingData>(target);
}

void BindingData::RegisterExternalReferences(
ExternalReferenceRegistry* registry) {
registry->Register(ReadPackageJSON);
}

} // namespace modules
} // namespace node

NODE_BINDING_CONTEXT_AWARE_INTERNAL(
modules, node::modules::BindingData::CreatePerContextProperties)
NODE_BINDING_PER_ISOLATE_INIT(
modules, node::modules::BindingData::CreatePerIsolateProperties)
NODE_BINDING_EXTERNAL_REFERENCE(
modules, node::modules::BindingData::RegisterExternalReferences)
Loading

0 comments on commit ba0be9b

Please sign in to comment.