diff --git a/src/node_external_reference.h b/src/node_external_reference.h index ae37094c8e117e..ae2422f919ad99 100644 --- a/src/node_external_reference.h +++ b/src/node_external_reference.h @@ -27,6 +27,11 @@ using CFunctionCallbackWithStrings = const v8::FastOneByteString& base); using CFunctionWithUint32 = uint32_t (*)(v8::Local, const uint32_t input); +using CFunctionCallbackWithStringOptions = + bool (*)(v8::Local, + const v8::FastOneByteString& input, + // NOLINTNEXTLINE(runtime/references) This is V8 api. + v8::FastApiCallbackOptions& options); // This class manages the external references from the V8 heap // to the C++ addresses in Node.js. @@ -41,6 +46,7 @@ class ExternalReferenceRegistry { V(CFunctionCallbackWithInt64) \ V(CFunctionCallbackWithBool) \ V(CFunctionCallbackWithString) \ + V(CFunctionCallbackWithStringOptions) \ V(CFunctionCallbackWithStrings) \ V(CFunctionWithUint32) \ V(const v8::CFunctionInfo*) \ diff --git a/src/node_file.cc b/src/node_file.cc index aca7ec82101a60..43e4145e6193ab 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -31,6 +31,7 @@ #include "node_stat_watcher.h" #include "permission/permission.h" #include "util-inl.h" +#include "v8-fast-api-calls.h" #include "tracing/trace_event.h" @@ -57,8 +58,11 @@ namespace fs { using v8::Array; using v8::BigInt; +using v8::CFunction; using v8::Context; using v8::EscapableHandleScope; +using v8::FastApiCallbackOptions; +using v8::FastOneByteString; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -1095,6 +1099,46 @@ static void ExistsSync(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(err == 0); } +bool FastExistsSync(v8::Local recv, + const FastOneByteString& string, + // NOLINTNEXTLINE(runtime/references) This is V8 api. + FastApiCallbackOptions& options) { + Environment* env = Environment::GetCurrent(recv->GetCreationContextChecked()); + + MaybeStackBuffer path; + + path.AllocateSufficientStorage(string.length + 1); + memcpy(path.out(), string.data, string.length); + path.SetLengthAndZeroTerminate(string.length); + + if (UNLIKELY(!env->permission()->is_granted( + permission::PermissionScope::kFileSystemRead, path.ToStringView()))) { + options.fallback = true; + return false; + } + + uv_fs_t req; + auto make = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); }); + FS_SYNC_TRACE_BEGIN(access); + int err = uv_fs_access(nullptr, &req, path.out(), 0, nullptr); + FS_SYNC_TRACE_END(access); + +#ifdef _WIN32 + // In case of an invalid symlink, `uv_fs_access` on win32 + // will **not** return an error and is therefore not enough. + // Double check with `uv_fs_stat()`. + if (err == 0) { + FS_SYNC_TRACE_BEGIN(stat); + err = uv_fs_stat(nullptr, &req, path.out(), nullptr); + FS_SYNC_TRACE_END(stat); + } +#endif // _WIN32 + + return err == 0; +} + +CFunction fast_exists_sync_(CFunction::Make(FastExistsSync)); + // Used to speed up module loading. Returns an array [string, boolean] static void InternalModuleReadJSON(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -3385,7 +3429,7 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data, SetMethod(isolate, target, "accessSync", AccessSync); SetMethod(isolate, target, "close", Close); SetMethod(isolate, target, "closeSync", CloseSync); - SetMethod(isolate, target, "existsSync", ExistsSync); + SetFastMethod(isolate, target, "existsSync", ExistsSync, &fast_exists_sync_); SetMethod(isolate, target, "open", Open); SetMethod(isolate, target, "openSync", OpenSync); SetMethod(isolate, target, "openFileHandle", OpenFileHandle); @@ -3512,6 +3556,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(Close); registry->Register(CloseSync); registry->Register(ExistsSync); + registry->Register(FastExistsSync); + registry->Register(fast_exists_sync_.GetTypeInfo()); registry->Register(Open); registry->Register(OpenSync); registry->Register(OpenFileHandle); diff --git a/test/parallel/test-fs-exists.js b/test/parallel/test-fs-exists.js index 857f3f26174549..ad939d6fb5899a 100644 --- a/test/parallel/test-fs-exists.js +++ b/test/parallel/test-fs-exists.js @@ -54,3 +54,12 @@ assert(!fs.existsSync(`${f}-NO`)); assert(!fs.existsSync()); assert(!fs.existsSync({})); assert(!fs.existsSync(new URL('https://foo'))); + +{ + // This test is to ensure that the v8 fast api works. + const oneBytePath = 'hello.txt'; + for (let i = 0; i < 1e5; i++) { + assert(!fs.existsSync(oneBytePath)); + assert(fs.existsSync(__filename)); + } +}