diff --git a/lib/module.js b/lib/module.js index b992ca8faf3add..1240df6586b510 100644 --- a/lib/module.js +++ b/lib/module.js @@ -10,6 +10,7 @@ const fs = require('fs'); const path = require('path'); const internalModuleReadFile = process.binding('fs').internalModuleReadFile; const internalModuleStat = process.binding('fs').internalModuleStat; +const internalModuleRealpath = process.binding('fs').internalModuleRealpath; const splitRe = process.platform === 'win32' ? /[\/\\]/ : /\//; const isIndexRe = /^index\.\w+?$/; @@ -438,9 +439,20 @@ Module._extensions['.json'] = function(module, filename) { }; +const nativeModuleCache = {}; + //Native extension for .node Module._extensions['.node'] = function(module, filename) { - return process.dlopen(module, path._makeLong(filename)); + const normalizedPath = internalModuleRealpath(path._makeLong(filename)); + const cachedNativeModule = nativeModuleCache[normalizedPath]; + + if (cachedNativeModule) { + module.exports = cachedNativeModule; + return; + } + + process.dlopen(module, path._makeLong(filename)); + nativeModuleCache[normalizedPath] = module.exports; }; diff --git a/src/node_file.cc b/src/node_file.cc index e63dd3fd37d978..c39f44225eb705 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -581,6 +581,25 @@ static void InternalModuleStat(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(rc); } +static void InternalModuleRealpath(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsString()); + node::Utf8Value path(env->isolate(), args[0]); + + uv_fs_t req; + int rc = uv_fs_realpath(env->event_loop(), &req, *path, nullptr); + if (rc == 0) { + const char* r = static_cast(req.ptr); + Local str = String::NewFromUtf8(env->isolate(), + r, v8::NewStringType::kNormal).ToLocalChecked(); + args.GetReturnValue().Set(str); + } else { + args.GetReturnValue().Set(args[0]); + } + uv_fs_req_cleanup(&req); +} + static void Stat(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -1422,6 +1441,7 @@ void InitFs(Local target, env->SetMethod(target, "readdir", ReadDir); env->SetMethod(target, "internalModuleReadFile", InternalModuleReadFile); env->SetMethod(target, "internalModuleStat", InternalModuleStat); + env->SetMethod(target, "internalModuleRealpath", InternalModuleRealpath); env->SetMethod(target, "stat", Stat); env->SetMethod(target, "lstat", LStat); env->SetMethod(target, "fstat", FStat); diff --git a/test/addons/clear-cache/binding.cc b/test/addons/clear-cache/binding.cc new file mode 100644 index 00000000000000..cdf9904e3f8d47 --- /dev/null +++ b/test/addons/clear-cache/binding.cc @@ -0,0 +1,13 @@ +#include +#include + +void Method(const v8::FunctionCallbackInfo& args) { + v8::Isolate* isolate = args.GetIsolate(); + args.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, "world")); +} + +void init(v8::Local target) { + NODE_SET_METHOD(target, "hello", Method); +} + +NODE_MODULE(binding, init); diff --git a/test/addons/clear-cache/binding.gyp b/test/addons/clear-cache/binding.gyp new file mode 100644 index 00000000000000..3bfb84493f3e87 --- /dev/null +++ b/test/addons/clear-cache/binding.gyp @@ -0,0 +1,8 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'sources': [ 'binding.cc' ] + } + ] +} diff --git a/test/addons/clear-cache/test.js b/test/addons/clear-cache/test.js new file mode 100644 index 00000000000000..eb18e7cdd6e8fc --- /dev/null +++ b/test/addons/clear-cache/test.js @@ -0,0 +1,13 @@ +'use strict'; +require('../../common'); +const assert = require('assert'); + +function clearCache() { + Object.keys(require.cache).forEach((key) => delete require.cache[key]); +} + +const val1 = require('./build/release/binding.node').hello; +clearCache(); +const val2 = require('./build/release/binding.node').hello; + +assert(val1 == val2);