mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
PR-URL: https://github.com/nodejs/node/pull/56962 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
678 lines
23 KiB
C++
678 lines
23 KiB
C++
#include "node_modules.h"
|
|
#include <cstdio>
|
|
#include "base_object-inl.h"
|
|
#include "compile_cache.h"
|
|
#include "node_errors.h"
|
|
#include "node_external_reference.h"
|
|
#include "node_url.h"
|
|
#include "path.h"
|
|
#include "permission/permission.h"
|
|
#include "permission/permission_base.h"
|
|
#include "util-inl.h"
|
|
#include "v8-fast-api-calls.h"
|
|
#include "v8-function-callback.h"
|
|
#include "v8-primitive.h"
|
|
#include "v8-value.h"
|
|
#include "v8.h"
|
|
|
|
#include "simdjson.h"
|
|
|
|
namespace node {
|
|
namespace modules {
|
|
|
|
using v8::Array;
|
|
using v8::Context;
|
|
using v8::External;
|
|
using v8::FunctionCallbackInfo;
|
|
using v8::HandleScope;
|
|
using v8::Integer;
|
|
using v8::Isolate;
|
|
using v8::Local;
|
|
using v8::LocalVector;
|
|
using v8::Name;
|
|
using v8::NewStringType;
|
|
using v8::Null;
|
|
using v8::Object;
|
|
using v8::ObjectTemplate;
|
|
using v8::Primitive;
|
|
using v8::String;
|
|
using v8::Undefined;
|
|
using v8::Value;
|
|
|
|
void BindingData::MemoryInfo(MemoryTracker* tracker) const {
|
|
// Do nothing
|
|
}
|
|
|
|
BindingData::BindingData(Realm* realm,
|
|
v8::Local<v8::Object> object,
|
|
InternalFieldInfo* info)
|
|
: SnapshotableObject(realm, object, type_int) {}
|
|
|
|
bool BindingData::PrepareForSerialization(v8::Local<v8::Context> context,
|
|
v8::SnapshotCreator* creator) {
|
|
// 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);
|
|
HandleScope scope(context->GetIsolate());
|
|
Realm* realm = Realm::GetCurrent(context);
|
|
BindingData* binding = realm->AddBindingData<BindingData>(holder);
|
|
CHECK_NOT_NULL(binding);
|
|
}
|
|
|
|
Local<Array> BindingData::PackageConfig::Serialize(Realm* realm) const {
|
|
auto isolate = realm->isolate();
|
|
const auto ToString = [isolate](std::string_view input) -> Local<Primitive> {
|
|
return String::NewFromUtf8(
|
|
isolate, input.data(), NewStringType::kNormal, input.size())
|
|
.ToLocalChecked();
|
|
};
|
|
Local<Value> values[6] = {
|
|
name.has_value() ? ToString(*name) : Undefined(isolate),
|
|
main.has_value() ? ToString(*main) : Undefined(isolate),
|
|
ToString(type),
|
|
imports.has_value() ? ToString(*imports) : Undefined(isolate),
|
|
exports.has_value() ? ToString(*exports) : Undefined(isolate),
|
|
ToString(file_path),
|
|
};
|
|
return Array::New(isolate, values, 6);
|
|
}
|
|
|
|
const BindingData::PackageConfig* BindingData::GetPackageJSON(
|
|
Realm* realm, std::string_view path, ErrorContext* error_context) {
|
|
auto binding_data = realm->GetBindingData<BindingData>();
|
|
|
|
auto cache_entry = binding_data->package_configs_.find(path.data());
|
|
if (cache_entry != binding_data->package_configs_.end()) {
|
|
return &cache_entry->second;
|
|
}
|
|
|
|
PackageConfig package_config{};
|
|
package_config.file_path = path;
|
|
// No need to exclude BOM since simdjson will skip it.
|
|
if (ReadFileSync(&package_config.raw_json, path.data()) < 0) {
|
|
return nullptr;
|
|
}
|
|
// In some systems, std::string is annotated to generate an
|
|
// AddressSanitizer: container-overflow error when reading beyond the end of
|
|
// the string even when we are still within the capacity of the string.
|
|
// https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow
|
|
// https://github.com/nodejs/node/issues/55584
|
|
// The next lines are a workaround to avoid this false positive.
|
|
size_t json_length = package_config.raw_json.size();
|
|
package_config.raw_json.append(simdjson::SIMDJSON_PADDING, ' ');
|
|
simdjson::padded_string_view json_view(package_config.raw_json.data(),
|
|
json_length,
|
|
package_config.raw_json.size());
|
|
// End of workaround
|
|
|
|
simdjson::ondemand::document document;
|
|
simdjson::ondemand::object main_object;
|
|
simdjson::error_code error =
|
|
binding_data->json_parser.iterate(json_view).get(document);
|
|
|
|
const auto throw_invalid_package_config = [error_context, path, realm]() {
|
|
if (error_context == nullptr) {
|
|
THROW_ERR_INVALID_PACKAGE_CONFIG(
|
|
realm->isolate(), "Invalid package config %s.", path.data());
|
|
} else if (error_context->base.has_value()) {
|
|
auto file_url = ada::parse(error_context->base.value());
|
|
CHECK(file_url);
|
|
auto file_path = url::FileURLToPath(realm->env(), *file_url);
|
|
CHECK(file_path.has_value());
|
|
THROW_ERR_INVALID_PACKAGE_CONFIG(
|
|
realm->isolate(),
|
|
"Invalid package config %s while importing \"%s\" from %s.",
|
|
path.data(),
|
|
error_context->specifier.c_str(),
|
|
file_path->c_str());
|
|
} else {
|
|
THROW_ERR_INVALID_PACKAGE_CONFIG(
|
|
realm->isolate(), "Invalid package config %s.", path.data());
|
|
}
|
|
|
|
return nullptr;
|
|
};
|
|
|
|
if (error || document.get_object().get(main_object)) {
|
|
return throw_invalid_package_config();
|
|
}
|
|
|
|
simdjson::ondemand::raw_json_string key;
|
|
simdjson::ondemand::value value;
|
|
std::string_view field_value;
|
|
simdjson::ondemand::json_type field_type;
|
|
|
|
for (auto field : main_object) {
|
|
// Throw error if getting key or value fails.
|
|
if (field.key().get(key) || field.value().get(value)) {
|
|
return throw_invalid_package_config();
|
|
}
|
|
|
|
// based on coverity using key with == derefs the raw value
|
|
// avoid derefing if its null
|
|
if (key.raw() == nullptr) continue;
|
|
|
|
if (key == "name") {
|
|
// Though there is a key "name" with a corresponding value,
|
|
// the value may not be a string or could be an invalid JSON string
|
|
if (value.get_string(package_config.name)) {
|
|
return throw_invalid_package_config();
|
|
}
|
|
} else if (key == "main") {
|
|
// Omit all non-string values
|
|
USE(value.get_string(package_config.main));
|
|
} else if (key == "exports") {
|
|
if (value.type().get(field_type)) {
|
|
return throw_invalid_package_config();
|
|
}
|
|
switch (field_type) {
|
|
case simdjson::ondemand::json_type::object:
|
|
case simdjson::ondemand::json_type::array: {
|
|
if (value.raw_json().get(field_value)) {
|
|
return throw_invalid_package_config();
|
|
}
|
|
package_config.exports = field_value;
|
|
break;
|
|
}
|
|
case simdjson::ondemand::json_type::string: {
|
|
if (value.get_string(package_config.exports)) {
|
|
return throw_invalid_package_config();
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
} else if (key == "imports") {
|
|
if (value.type().get(field_type)) {
|
|
return throw_invalid_package_config();
|
|
}
|
|
switch (field_type) {
|
|
case simdjson::ondemand::json_type::array:
|
|
case simdjson::ondemand::json_type::object: {
|
|
if (value.raw_json().get(field_value)) {
|
|
return throw_invalid_package_config();
|
|
}
|
|
package_config.imports = field_value;
|
|
break;
|
|
}
|
|
case simdjson::ondemand::json_type::string: {
|
|
if (value.get_string(package_config.imports)) {
|
|
return throw_invalid_package_config();
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
} else if (key == "type") {
|
|
if (value.get_string().get(field_value)) {
|
|
return throw_invalid_package_config();
|
|
}
|
|
// Only update type if it is "commonjs" or "module"
|
|
// The default value is "none" for backward compatibility.
|
|
if (field_value == "commonjs" || field_value == "module") {
|
|
package_config.type = field_value;
|
|
}
|
|
} else if (key == "scripts") {
|
|
if (value.type().get(field_type)) {
|
|
return throw_invalid_package_config();
|
|
}
|
|
switch (field_type) {
|
|
case simdjson::ondemand::json_type::object: {
|
|
if (value.raw_json().get(field_value)) {
|
|
return throw_invalid_package_config();
|
|
}
|
|
package_config.scripts = field_value;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// package_config could be quite large, so we should move it instead of
|
|
// copying it.
|
|
auto cached = binding_data->package_configs_.insert(
|
|
{std::string(path), std::move(package_config)});
|
|
|
|
return &cached.first->second;
|
|
}
|
|
|
|
void BindingData::ReadPackageJSON(const FunctionCallbackInfo<Value>& args) {
|
|
CHECK_GE(args.Length(), 1); // path, [is_esm, base, specifier]
|
|
CHECK(args[0]->IsString()); // path
|
|
|
|
Realm* realm = Realm::GetCurrent(args);
|
|
auto isolate = realm->isolate();
|
|
|
|
BufferValue path(isolate, args[0]);
|
|
bool is_esm = args[1]->IsTrue();
|
|
auto error_context = ErrorContext();
|
|
if (is_esm) {
|
|
CHECK(args[2]->IsUndefined() || args[2]->IsString()); // base
|
|
CHECK(args[3]->IsString()); // specifier
|
|
|
|
if (args[2]->IsString()) {
|
|
Utf8Value base_value(isolate, args[2]);
|
|
error_context.base = base_value.ToString();
|
|
}
|
|
Utf8Value specifier(isolate, args[3]);
|
|
error_context.specifier = specifier.ToString();
|
|
}
|
|
|
|
THROW_IF_INSUFFICIENT_PERMISSIONS(
|
|
realm->env(),
|
|
permission::PermissionScope::kFileSystemRead,
|
|
path.ToStringView());
|
|
|
|
ToNamespacedPath(realm->env(), &path);
|
|
auto package_json = GetPackageJSON(
|
|
realm, path.ToStringView(), is_esm ? &error_context : nullptr);
|
|
|
|
if (package_json == nullptr) {
|
|
return;
|
|
}
|
|
|
|
args.GetReturnValue().Set(package_json->Serialize(realm));
|
|
}
|
|
|
|
const BindingData::PackageConfig* BindingData::TraverseParent(
|
|
Realm* realm, const std::filesystem::path& check_path) {
|
|
std::filesystem::path current_path = check_path;
|
|
auto env = realm->env();
|
|
const bool is_permissions_enabled = env->permission()->enabled();
|
|
|
|
do {
|
|
current_path = current_path.parent_path();
|
|
|
|
// We don't need to try "/"
|
|
if (current_path.parent_path() == current_path) {
|
|
break;
|
|
}
|
|
|
|
// Stop the search when the process doesn't have permissions
|
|
// to walk upwards
|
|
if (is_permissions_enabled &&
|
|
!env->permission()->is_granted(
|
|
env,
|
|
permission::PermissionScope::kFileSystemRead,
|
|
current_path.generic_string())) [[unlikely]] {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the path ends with `/node_modules`
|
|
if (current_path.generic_string().ends_with("/node_modules")) {
|
|
return nullptr;
|
|
}
|
|
|
|
auto package_json_path = current_path / "package.json";
|
|
auto package_json =
|
|
GetPackageJSON(realm, package_json_path.string(), nullptr);
|
|
if (package_json != nullptr) {
|
|
return package_json;
|
|
}
|
|
} while (true);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void BindingData::GetNearestParentPackageJSON(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
CHECK_GE(args.Length(), 1);
|
|
CHECK(args[0]->IsString());
|
|
|
|
Realm* realm = Realm::GetCurrent(args);
|
|
BufferValue path_value(realm->isolate(), args[0]);
|
|
// Check if the path has a trailing slash. If so, add it after
|
|
// ToNamespacedPath() as it will be deleted by ToNamespacedPath()
|
|
bool slashCheck = path_value.ToStringView().ends_with(kPathSeparator);
|
|
|
|
ToNamespacedPath(realm->env(), &path_value);
|
|
|
|
std::string path_value_str = path_value.ToString();
|
|
if (slashCheck) {
|
|
path_value_str.push_back(kPathSeparator);
|
|
}
|
|
|
|
std::filesystem::path path;
|
|
|
|
#ifdef _WIN32
|
|
std::wstring wide_path = ConvertToWideString(path_value_str, GetACP());
|
|
path = std::filesystem::path(wide_path);
|
|
#else
|
|
path = std::filesystem::path(path_value_str);
|
|
#endif
|
|
|
|
auto package_json = TraverseParent(realm, path);
|
|
|
|
if (package_json != nullptr) {
|
|
args.GetReturnValue().Set(package_json->Serialize(realm));
|
|
}
|
|
}
|
|
|
|
void BindingData::GetNearestParentPackageJSONType(
|
|
const FunctionCallbackInfo<Value>& args) {
|
|
CHECK_GE(args.Length(), 1);
|
|
CHECK(args[0]->IsString());
|
|
|
|
Realm* realm = Realm::GetCurrent(args);
|
|
BufferValue path_value(realm->isolate(), args[0]);
|
|
// Check if the path has a trailing slash. If so, add it after
|
|
// ToNamespacedPath() as it will be deleted by ToNamespacedPath()
|
|
bool slashCheck = path_value.ToStringView().ends_with(kPathSeparator);
|
|
|
|
ToNamespacedPath(realm->env(), &path_value);
|
|
|
|
std::string path_value_str = path_value.ToString();
|
|
if (slashCheck) {
|
|
path_value_str.push_back(kPathSeparator);
|
|
}
|
|
|
|
std::filesystem::path path;
|
|
|
|
#ifdef _WIN32
|
|
std::wstring wide_path = ConvertToWideString(path_value_str, GetACP());
|
|
path = std::filesystem::path(wide_path);
|
|
#else
|
|
path = std::filesystem::path(path_value_str);
|
|
#endif
|
|
|
|
auto package_json = TraverseParent(realm, path);
|
|
|
|
if (package_json == nullptr) {
|
|
return;
|
|
}
|
|
|
|
Local<Value> value;
|
|
if (ToV8Value(realm->context(), package_json->type).ToLocal(&value)) {
|
|
args.GetReturnValue().Set(value);
|
|
}
|
|
}
|
|
|
|
void BindingData::GetPackageScopeConfig(
|
|
const FunctionCallbackInfo<Value>& args) {
|
|
CHECK_GE(args.Length(), 1);
|
|
CHECK(args[0]->IsString());
|
|
|
|
Realm* realm = Realm::GetCurrent(args);
|
|
Utf8Value resolved(realm->isolate(), args[0]);
|
|
auto package_json_url_base = ada::parse(resolved.ToStringView());
|
|
if (!package_json_url_base) {
|
|
url::ThrowInvalidURL(realm->env(), resolved.ToStringView(), std::nullopt);
|
|
return;
|
|
}
|
|
auto package_json_url =
|
|
ada::parse("./package.json", &package_json_url_base.value());
|
|
if (!package_json_url) {
|
|
url::ThrowInvalidURL(realm->env(), "./package.json", resolved.ToString());
|
|
return;
|
|
}
|
|
|
|
std::string_view node_modules_package_path = "/node_modules/package.json";
|
|
auto error_context = ErrorContext();
|
|
error_context.is_esm = true;
|
|
|
|
// TODO(@anonrig): Rewrite this function and avoid calling URL parser.
|
|
while (true) {
|
|
auto pathname = package_json_url->get_pathname();
|
|
if (pathname.ends_with(node_modules_package_path)) {
|
|
break;
|
|
}
|
|
|
|
auto file_url = url::FileURLToPath(realm->env(), *package_json_url);
|
|
if (!file_url) {
|
|
url::ThrowInvalidURL(realm->env(), resolved.ToStringView(), std::nullopt);
|
|
return;
|
|
}
|
|
error_context.specifier = resolved.ToString();
|
|
auto package_json = GetPackageJSON(realm, *file_url, &error_context);
|
|
if (package_json != nullptr) {
|
|
return args.GetReturnValue().Set(package_json->Serialize(realm));
|
|
}
|
|
|
|
auto last_href = std::string(package_json_url->get_href());
|
|
auto last_pathname = std::string(package_json_url->get_pathname());
|
|
package_json_url = ada::parse("../package.json", &package_json_url.value());
|
|
if (!package_json_url) {
|
|
url::ThrowInvalidURL(realm->env(), "../package.json", last_href);
|
|
return;
|
|
}
|
|
|
|
// Terminates at root where ../package.json equals ../../package.json
|
|
// (can't just check "/package.json" for Windows support).
|
|
if (package_json_url->get_pathname() == last_pathname) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
auto package_json_url_as_path =
|
|
url::FileURLToPath(realm->env(), *package_json_url);
|
|
CHECK(package_json_url_as_path);
|
|
Local<Value> ret;
|
|
if (ToV8Value(realm->context(), *package_json_url_as_path, realm->isolate())
|
|
.ToLocal(&ret)) {
|
|
args.GetReturnValue().Set(ret);
|
|
}
|
|
}
|
|
|
|
void FlushCompileCache(const FunctionCallbackInfo<Value>& args) {
|
|
Isolate* isolate = args.GetIsolate();
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
Environment* env = Environment::GetCurrent(context);
|
|
|
|
if (!args[0]->IsBoolean() && !args[0]->IsUndefined()) {
|
|
THROW_ERR_INVALID_ARG_TYPE(env,
|
|
"keepDeserializedCache should be a boolean");
|
|
return;
|
|
}
|
|
Debug(env,
|
|
DebugCategory::COMPILE_CACHE,
|
|
"[compile cache] module.flushCompileCache() requested.\n");
|
|
env->FlushCompileCache();
|
|
Debug(env,
|
|
DebugCategory::COMPILE_CACHE,
|
|
"[compile cache] module.flushCompileCache() finished.\n");
|
|
}
|
|
|
|
void EnableCompileCache(const FunctionCallbackInfo<Value>& args) {
|
|
Isolate* isolate = args.GetIsolate();
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
Environment* env = Environment::GetCurrent(context);
|
|
if (!args[0]->IsString()) {
|
|
THROW_ERR_INVALID_ARG_TYPE(env, "cacheDir should be a string");
|
|
return;
|
|
}
|
|
Utf8Value value(isolate, args[0]);
|
|
CompileCacheEnableResult result = env->EnableCompileCache(*value);
|
|
Local<Value> values[3];
|
|
values[0] = v8::Integer::New(isolate, static_cast<uint8_t>(result.status));
|
|
if (ToV8Value(context, result.message).ToLocal(&values[1]) &&
|
|
ToV8Value(context, result.cache_directory).ToLocal(&values[2])) {
|
|
args.GetReturnValue().Set(
|
|
Array::New(isolate, &values[0], arraysize(values)));
|
|
}
|
|
}
|
|
|
|
void GetCompileCacheDir(const FunctionCallbackInfo<Value>& args) {
|
|
Isolate* isolate = args.GetIsolate();
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
Environment* env = Environment::GetCurrent(context);
|
|
if (!env->use_compile_cache()) {
|
|
args.GetReturnValue().Set(v8::String::Empty(isolate));
|
|
return;
|
|
}
|
|
|
|
Local<Value> ret;
|
|
if (ToV8Value(context, env->compile_cache_handler()->cache_dir())
|
|
.ToLocal(&ret)) {
|
|
args.GetReturnValue().Set(ret);
|
|
}
|
|
}
|
|
|
|
void GetCompileCacheEntry(const FunctionCallbackInfo<Value>& args) {
|
|
Isolate* isolate = args.GetIsolate();
|
|
CHECK(args[0]->IsString()); // TODO(joyeecheung): accept buffer.
|
|
CHECK(args[1]->IsString());
|
|
CHECK(args[2]->IsUint32());
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
Environment* env = Environment::GetCurrent(context);
|
|
if (!env->use_compile_cache()) {
|
|
return;
|
|
}
|
|
Local<String> source = args[0].As<String>();
|
|
Local<String> filename = args[1].As<String>();
|
|
CachedCodeType type =
|
|
static_cast<CachedCodeType>(args[2].As<v8::Uint32>()->Value());
|
|
auto* cache_entry =
|
|
env->compile_cache_handler()->GetOrInsert(source, filename, type);
|
|
if (cache_entry == nullptr) {
|
|
return;
|
|
}
|
|
|
|
v8::LocalVector<v8::Name> names(isolate,
|
|
{FIXED_ONE_BYTE_STRING(isolate, "external")});
|
|
v8::LocalVector<v8::Value> values(isolate,
|
|
{v8::External::New(isolate, cache_entry)});
|
|
if (cache_entry->cache != nullptr) {
|
|
Debug(env,
|
|
DebugCategory::COMPILE_CACHE,
|
|
"[compile cache] retrieving transpile cache for %s %s...",
|
|
cache_entry->type_name(),
|
|
cache_entry->source_filename);
|
|
|
|
std::string_view cache(
|
|
reinterpret_cast<const char*>(cache_entry->cache->data),
|
|
cache_entry->cache->length);
|
|
Local<Value> transpiled;
|
|
// TODO(joyeecheung): convert with simdutf and into external strings
|
|
if (!ToV8Value(context, cache).ToLocal(&transpiled)) {
|
|
Debug(env, DebugCategory::COMPILE_CACHE, "failed\n");
|
|
return;
|
|
} else {
|
|
Debug(env, DebugCategory::COMPILE_CACHE, "success\n");
|
|
}
|
|
names.push_back(FIXED_ONE_BYTE_STRING(isolate, "transpiled"));
|
|
values.push_back(transpiled);
|
|
} else {
|
|
Debug(env,
|
|
DebugCategory::COMPILE_CACHE,
|
|
"[compile cache] no transpile cache for %s %s\n",
|
|
cache_entry->type_name(),
|
|
cache_entry->source_filename);
|
|
}
|
|
args.GetReturnValue().Set(Object::New(
|
|
isolate, v8::Null(isolate), names.data(), values.data(), names.size()));
|
|
}
|
|
|
|
void SaveCompileCacheEntry(const FunctionCallbackInfo<Value>& args) {
|
|
Isolate* isolate = args.GetIsolate();
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
Environment* env = Environment::GetCurrent(context);
|
|
DCHECK(env->use_compile_cache());
|
|
CHECK(args[0]->IsExternal());
|
|
CHECK(args[1]->IsString()); // TODO(joyeecheung): accept buffer.
|
|
auto* cache_entry =
|
|
static_cast<CompileCacheEntry*>(args[0].As<External>()->Value());
|
|
Utf8Value utf8(isolate, args[1].As<String>());
|
|
env->compile_cache_handler()->MaybeSave(cache_entry, utf8.ToStringView());
|
|
}
|
|
|
|
void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
|
|
Local<ObjectTemplate> target) {
|
|
Isolate* isolate = isolate_data->isolate();
|
|
SetMethod(isolate, target, "readPackageJSON", ReadPackageJSON);
|
|
SetMethod(isolate,
|
|
target,
|
|
"getNearestParentPackageJSONType",
|
|
GetNearestParentPackageJSONType);
|
|
SetMethod(isolate,
|
|
target,
|
|
"getNearestParentPackageJSON",
|
|
GetNearestParentPackageJSON);
|
|
SetMethod(isolate, target, "getPackageScopeConfig", GetPackageScopeConfig);
|
|
SetMethod(isolate, target, "enableCompileCache", EnableCompileCache);
|
|
SetMethod(isolate, target, "getCompileCacheDir", GetCompileCacheDir);
|
|
SetMethod(isolate, target, "flushCompileCache", FlushCompileCache);
|
|
SetMethod(isolate, target, "getCompileCacheEntry", GetCompileCacheEntry);
|
|
SetMethod(isolate, target, "saveCompileCacheEntry", SaveCompileCacheEntry);
|
|
}
|
|
|
|
void BindingData::CreatePerContextProperties(Local<Object> target,
|
|
Local<Value> unused,
|
|
Local<Context> context,
|
|
void* priv) {
|
|
Realm* realm = Realm::GetCurrent(context);
|
|
realm->AddBindingData<BindingData>(target);
|
|
|
|
Isolate* isolate = context->GetIsolate();
|
|
LocalVector<Value> compile_cache_status_values(isolate);
|
|
|
|
#define V(status) \
|
|
compile_cache_status_values.push_back( \
|
|
FIXED_ONE_BYTE_STRING(isolate, #status));
|
|
COMPILE_CACHE_STATUS(V)
|
|
#undef V
|
|
|
|
USE(target->Set(context,
|
|
FIXED_ONE_BYTE_STRING(isolate, "compileCacheStatus"),
|
|
Array::New(isolate,
|
|
compile_cache_status_values.data(),
|
|
compile_cache_status_values.size())));
|
|
|
|
LocalVector<Name> cached_code_type_keys(isolate);
|
|
LocalVector<Value> cached_code_type_values(isolate);
|
|
|
|
#define V(type, value) \
|
|
cached_code_type_keys.push_back(FIXED_ONE_BYTE_STRING(isolate, #type)); \
|
|
cached_code_type_values.push_back(Integer::New(isolate, value)); \
|
|
DCHECK_EQ(value, cached_code_type_values.size() - 1);
|
|
CACHED_CODE_TYPES(V)
|
|
#undef V
|
|
|
|
USE(target->Set(context,
|
|
FIXED_ONE_BYTE_STRING(isolate, "cachedCodeTypes"),
|
|
Object::New(isolate,
|
|
Null(isolate),
|
|
cached_code_type_keys.data(),
|
|
cached_code_type_values.data(),
|
|
cached_code_type_keys.size())));
|
|
}
|
|
|
|
void BindingData::RegisterExternalReferences(
|
|
ExternalReferenceRegistry* registry) {
|
|
registry->Register(ReadPackageJSON);
|
|
registry->Register(GetNearestParentPackageJSONType);
|
|
registry->Register(GetNearestParentPackageJSON);
|
|
registry->Register(GetPackageScopeConfig);
|
|
registry->Register(EnableCompileCache);
|
|
registry->Register(GetCompileCacheDir);
|
|
registry->Register(FlushCompileCache);
|
|
registry->Register(GetCompileCacheEntry);
|
|
registry->Register(SaveCompileCacheEntry);
|
|
}
|
|
|
|
} // 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)
|