mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
src: add JS APIs for compile cache and NODE_DISABLE_COMPILE_CACHE
This patch adds the following API for tools to enable compile cache dynamically and query its status. - module.enableCompileCache(cacheDir) - module.getCompileCacheDir() In addition this adds a NODE_DISABLE_COMPILE_CACHE environment variable to disable the code cache enabled by the APIs as an escape hatch to avoid unexpected/undesired effects of the compile cache (e.g. less precise test coverage). When the module.enableCompileCache() method is invoked without a specified directory, Node.js will use the value of the NODE_COMPILE_CACHE environment variable if it's set, or defaults to `path.join(os.tmpdir(), 'node-compile-cache')` otherwise. Therefore it's recommended for tools to call this method without specifying the directory to allow overrides. PR-URL: https://github.com/nodejs/node/pull/54501 Fixes: https://github.com/nodejs/node/issues/53639 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
This commit is contained in:
parent
4a0ec20a35
commit
ff5ef7083d
@ -2841,25 +2841,8 @@ added: v22.1.0
|
||||
|
||||
> Stability: 1.1 - Active Development
|
||||
|
||||
When set, whenever Node.js compiles a CommonJS or a ECMAScript Module,
|
||||
it will use on-disk [V8 code cache][] persisted in the specified directory
|
||||
to speed up the compilation. This may slow down the first load of a
|
||||
module graph, but subsequent loads of the same module graph may get
|
||||
a significant speedup if the contents of the modules do not change.
|
||||
|
||||
To clean up the generated code cache, simply remove the directory.
|
||||
It will be recreated the next time the same directory is used for
|
||||
`NODE_COMPILE_CACHE`.
|
||||
|
||||
Compilation cache generated by one version of Node.js may not be used
|
||||
by a different version of Node.js. Cache generated by different versions
|
||||
of Node.js will be stored separately if the same directory is used
|
||||
to persist the cache, so they can co-exist.
|
||||
|
||||
Caveat: currently when using this with [V8 JavaScript code coverage][], the
|
||||
coverage being collected by V8 may be less precise in functions that are
|
||||
deserialized from the code cache. It's recommended to turn this off when
|
||||
running tests to generate precise coverage.
|
||||
Enable the [module compile cache][] for the Node.js instance. See the documentation of
|
||||
[module compile cache][] for details.
|
||||
|
||||
### `NODE_DEBUG=module[,…]`
|
||||
|
||||
@ -2881,6 +2864,17 @@ added: v0.3.0
|
||||
|
||||
When set, colors will not be used in the REPL.
|
||||
|
||||
### `NODE_DISABLE_COMPILE_CACHE=1`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
> Stability: 1.1 - Active Development
|
||||
|
||||
Disable the [module compile cache][] for the Node.js instance. See the documentation of
|
||||
[module compile cache][] for details.
|
||||
|
||||
### `NODE_EXTRA_CA_CERTS=file`
|
||||
|
||||
<!-- YAML
|
||||
@ -3527,7 +3521,6 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
|
||||
[TypeScript type-stripping]: typescript.md#type-stripping
|
||||
[V8 Inspector integration for Node.js]: debugger.md#v8-inspector-integration-for-nodejs
|
||||
[V8 JavaScript code coverage]: https://v8project.blogspot.com/2017/12/javascript-code-coverage.html
|
||||
[V8 code cache]: https://v8.dev/blog/code-caching-for-devs
|
||||
[`"type"`]: packages.md#type
|
||||
[`--allow-child-process`]: #--allow-child-process
|
||||
[`--allow-fs-read`]: #--allow-fs-read
|
||||
@ -3581,6 +3574,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
|
||||
[filtering tests by name]: test.md#filtering-tests-by-name
|
||||
[jitless]: https://v8.dev/blog/jitless
|
||||
[libuv threadpool documentation]: https://docs.libuv.org/en/latest/threadpool.html
|
||||
[module compile cache]: module.md#module-compile-cache
|
||||
[remote code execution]: https://www.owasp.org/index.php/Code_Injection
|
||||
[running tests from the command line]: test.md#running-tests-from-the-command-line
|
||||
[scavenge garbage collector]: https://v8.dev/blog/orinoco-parallel-scavenger
|
||||
|
@ -64,6 +64,152 @@ const require = createRequire(import.meta.url);
|
||||
const siblingModule = require('./sibling-module');
|
||||
```
|
||||
|
||||
### `module.constants.compileCacheStatus`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
> Stability: 1.1 - Active Development
|
||||
|
||||
The following constants are returned as the `status` field in the object returned by
|
||||
[`module.enableCompileCache()`][] to indicate the result of the attempt to enable the
|
||||
[module compile cache][].
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Constant</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>ENABLED</code></td>
|
||||
<td>
|
||||
Node.js has enabled the compile cache successfully. The directory used to store the
|
||||
compile cache will be returned in the <code>directory</code> field in the
|
||||
returned object.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>ALREADY_ENABLED</code></td>
|
||||
<td>
|
||||
The compile cache has already been enabled before, either by a previous call to
|
||||
<code>module.enableCompileCache()</code>, or by the <code>NODE_COMPILE_CACHE=dir</code>
|
||||
environment variable. The directory used to store the
|
||||
compile cache will be returned in the <code>directory</code> field in the
|
||||
returned object.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>FAILED</code></td>
|
||||
<td>
|
||||
Node.js fails to enable the compile cache. This can be caused by the lack of
|
||||
permission to use the specified directory, or various kinds of file system errors.
|
||||
The detail of the failure will be returned in the <code>message</code> field in the
|
||||
returned object.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>DISABLED</code></td>
|
||||
<td>
|
||||
Node.js cannot enable the compile cache because the environment variable
|
||||
<code>NODE_DISABLE_COMPILE_CACHE=1</code> has been set.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### `module.enableCompileCache([cacheDir])`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
> Stability: 1.1 - Active Development
|
||||
|
||||
* `cacheDir` {string|undefined} Optional path to specify the directory where the compile cache
|
||||
will be stored/retrieved.
|
||||
* Returns: {Object}
|
||||
* `status` {integer} One of the [`module.constants.compileCacheStatus`][]
|
||||
* `message` {string|undefined} If Node.js cannot enable the compile cache, this contains
|
||||
the error message. Only set if `status` is `module.constants.compileCacheStatus.FAILED`.
|
||||
* `directory` {string|undefined} If the compile cache is enabled, this contains the directory
|
||||
where the compile cache is stored. Only set if `status` is
|
||||
`module.constants.compileCacheStatus.ENABLED` or
|
||||
`module.constants.compileCacheStatus.ALREADY_ENABLED`.
|
||||
|
||||
Enable [module compile cache][] in the current Node.js instance.
|
||||
|
||||
If `cacheDir` is not specified, Node.js will either use the directory specified by the
|
||||
[`NODE_COMPILE_CACHE=dir`][] environment variable if it's set, or use
|
||||
`path.join(os.tmpdir(), 'node-compile-cache')` otherwise. For general use cases, it's
|
||||
recommended to call `module.enableCompileCache()` without specifying the `cacheDir`,
|
||||
so that the directory can be overriden by the `NODE_COMPILE_CACHE` environment
|
||||
variable when necessary.
|
||||
|
||||
Since compile cache is supposed to be a quiet optimization that is not required for the
|
||||
application to be functional, this method is designed to not throw any exception when the
|
||||
compile cache cannot be enabled. Instead, it will return an object containing an error
|
||||
message in the `message` field to aid debugging.
|
||||
If compile cache is enabled successefully, the `directory` field in the returned object
|
||||
contains the path to the directory where the compile cache is stored. The `status`
|
||||
field in the returned object would be one of the `module.constants.compileCacheStatus`
|
||||
values to indicate the result of the attempt to enable the [module compile cache][].
|
||||
|
||||
This method only affects the current Node.js instance. To enable it in child worker threads,
|
||||
either call this method in child worker threads too, or set the
|
||||
`process.env.NODE_COMPILE_CACHE` value to compile cache directory so the behavior can
|
||||
be inheritend into the child workers. The directory can be obtained either from the
|
||||
`directory` field returned by this method, or with [`module.getCompileCacheDir()`][].
|
||||
|
||||
#### Module compile cache
|
||||
|
||||
<!-- YAML
|
||||
added: v22.1.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/54501
|
||||
description: add initial JavaScript APIs for runtime access.
|
||||
-->
|
||||
|
||||
The module compile cache can be enabled either using the [`module.enableCompileCache()`][]
|
||||
method or the [`NODE_COMPILE_CACHE=dir`][] environemnt variable. After it's enabled,
|
||||
whenever Node.js compiles a CommonJS or a ECMAScript Module, it will use on-disk
|
||||
[V8 code cache][] persisted in the specified directory to speed up the compilation.
|
||||
This may slow down the first load of a module graph, but subsequent loads of the same module
|
||||
graph may get a significant speedup if the contents of the modules do not change.
|
||||
|
||||
To clean up the generated compile cache on disk, simply remove the cache directory. The cache
|
||||
directory will be recreated the next time the same directory is used for for compile cache
|
||||
storage. To avoid filling up the disk with stale cache, it is recommended to use a directory
|
||||
under the [`os.tmpdir()`][]. If the compile cache is enabled by a call to
|
||||
[`module.enableCompileCache()`][] without specifying the directory, Node.js will use
|
||||
the [`NODE_DISABLE_COMPILE_CACHE=1`][] environment variable if it's set, or defaults
|
||||
to `path.join(os.tmpdir(), 'node-compile-cache')` otherwise. To locate the compile cache
|
||||
directory used by a running Node.js instance, use [`module.getCompileCacheDir()`][].
|
||||
|
||||
Currently when using the compile cache with [V8 JavaScript code coverage][], the
|
||||
coverage being collected by V8 may be less precise in functions that are
|
||||
deserialized from the code cache. It's recommended to turn this off when
|
||||
running tests to generate precise coverage.
|
||||
|
||||
The enabled module compile cache can be disabled by the [`NODE_DISABLE_COMPILE_CACHE=1`][]
|
||||
environment variable. This can be useful when the compile cache leads to unexpected or
|
||||
undesired behaviors (e.g. less precise test coverage).
|
||||
|
||||
Compilation cache generated by one version of Node.js can not be reused by a different
|
||||
version of Node.js. Cache generated by different versions of Node.js will be stored
|
||||
separately if the same base directory is used to persist the cache, so they can co-exist.
|
||||
|
||||
### `module.getCompileCacheDir()`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
> Stability: 1.1 - Active Development
|
||||
|
||||
* Returns: {string|undefined} Path to the [module compile cache][] directory if it is enabled,
|
||||
or `undefined` otherwise.
|
||||
|
||||
### `module.isBuiltin(moduleName)`
|
||||
|
||||
<!-- YAML
|
||||
@ -1055,22 +1201,31 @@ returned object contains the following keys:
|
||||
[Customization hooks]: #customization-hooks
|
||||
[ES Modules]: esm.md
|
||||
[Source map v3 format]: https://sourcemaps.info/spec.html#h.mofvlxcwqzej
|
||||
[V8 JavaScript code coverage]: https://v8project.blogspot.com/2017/12/javascript-code-coverage.html
|
||||
[V8 code cache]: https://v8.dev/blog/code-caching-for-devs
|
||||
[`"exports"`]: packages.md#exports
|
||||
[`--enable-source-maps`]: cli.md#--enable-source-maps
|
||||
[`ArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
|
||||
[`NODE_COMPILE_CACHE=dir`]: cli.md#node_compile_cachedir
|
||||
[`NODE_DISABLE_COMPILE_CACHE=1`]: cli.md#node_disable_compile_cache1
|
||||
[`NODE_V8_COVERAGE=dir`]: cli.md#node_v8_coveragedir
|
||||
[`SharedArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
|
||||
[`SourceMap`]: #class-modulesourcemap
|
||||
[`TypedArray`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
|
||||
[`Uint8Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
|
||||
[`initialize`]: #initialize
|
||||
[`module`]: modules.md#the-module-object
|
||||
[`module.constants.compileCacheStatus`]: #moduleconstantscompilecachestatus
|
||||
[`module.enableCompileCache()`]: #moduleenablecompilecachecachedir
|
||||
[`module.getCompileCacheDir()`]: #modulegetcompilecachedir
|
||||
[`module`]: #the-module-object
|
||||
[`os.tmpdir()`]: os.md#ostmpdir
|
||||
[`register`]: #moduleregisterspecifier-parenturl-options
|
||||
[`string`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String
|
||||
[`util.TextDecoder`]: util.md#class-utiltextdecoder
|
||||
[chain]: #chaining
|
||||
[hooks]: #customization-hooks
|
||||
[load hook]: #loadurl-context-nextload
|
||||
[module compile cache]: #module-compile-cache
|
||||
[module wrapper]: modules.md#the-module-wrapper
|
||||
[prefix-only modules]: modules.md#built-in-modules-with-mandatory-node-prefix
|
||||
[realm]: https://tc39.es/ecma262/#realm
|
||||
|
@ -4,6 +4,7 @@ const {
|
||||
ArrayPrototypeForEach,
|
||||
ArrayPrototypeIncludes,
|
||||
ObjectDefineProperty,
|
||||
ObjectFreeze,
|
||||
ObjectPrototypeHasOwnProperty,
|
||||
SafeMap,
|
||||
SafeSet,
|
||||
@ -28,10 +29,18 @@ const assert = require('internal/assert');
|
||||
|
||||
const { Buffer } = require('buffer');
|
||||
const { getOptionValue } = require('internal/options');
|
||||
const { setOwnProperty } = require('internal/util');
|
||||
const { setOwnProperty, getLazy } = require('internal/util');
|
||||
const { inspect } = require('internal/util/inspect');
|
||||
|
||||
const lazyTmpdir = getLazy(() => require('os').tmpdir());
|
||||
const { join } = path;
|
||||
|
||||
const { canParse: URLCanParse } = internalBinding('url');
|
||||
const {
|
||||
enableCompileCache: _enableCompileCache,
|
||||
getCompileCacheDir: _getCompileCacheDir,
|
||||
compileCacheStatus: _compileCacheStatus,
|
||||
} = internalBinding('modules');
|
||||
|
||||
let debug = require('internal/util/debuglog').debuglog('module', (fn) => {
|
||||
debug = fn;
|
||||
@ -431,11 +440,54 @@ function stringify(body) {
|
||||
return DECODER.decode(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable on-disk compiled cache for all user modules being complied in the current Node.js instance
|
||||
* after this method is called.
|
||||
* If cacheDir is undefined, defaults to the NODE_MODULE_CACHE environment variable.
|
||||
* If NODE_MODULE_CACHE isn't set, default to path.join(os.tmpdir(), 'node-compile-cache').
|
||||
* @param {string|undefined} cacheDir
|
||||
* @returns {{status: number, message?: string, directory?: string}}
|
||||
*/
|
||||
function enableCompileCache(cacheDir) {
|
||||
if (cacheDir === undefined) {
|
||||
cacheDir = join(lazyTmpdir(), 'node-compile-cache');
|
||||
}
|
||||
const nativeResult = _enableCompileCache(cacheDir);
|
||||
const result = { status: nativeResult[0] };
|
||||
if (nativeResult[1]) {
|
||||
result.message = nativeResult[1];
|
||||
}
|
||||
if (nativeResult[2]) {
|
||||
result.directory = nativeResult[2];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const compileCacheStatus = { __proto__: null };
|
||||
for (let i = 0; i < _compileCacheStatus.length; ++i) {
|
||||
compileCacheStatus[_compileCacheStatus[i]] = i;
|
||||
}
|
||||
ObjectFreeze(compileCacheStatus);
|
||||
const constants = { __proto__: null, compileCacheStatus };
|
||||
ObjectFreeze(constants);
|
||||
|
||||
/**
|
||||
* Get the compile cache directory if on-disk compile cache is enabled.
|
||||
* @returns {string|undefined} Path to the module compile cache directory if it is enabled,
|
||||
* or undefined otherwise.
|
||||
*/
|
||||
function getCompileCacheDir() {
|
||||
return _getCompileCacheDir() || undefined;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addBuiltinLibsToObject,
|
||||
assertBufferSource,
|
||||
constants,
|
||||
enableCompileCache,
|
||||
getBuiltinModule,
|
||||
getCjsConditions,
|
||||
getCompileCacheDir,
|
||||
initializeCjsConditions,
|
||||
isUnderNodeModules,
|
||||
loadBuiltinModule,
|
||||
|
@ -4,8 +4,16 @@ const { findSourceMap } = require('internal/source_map/source_map_cache');
|
||||
const { Module } = require('internal/modules/cjs/loader');
|
||||
const { register } = require('internal/modules/esm/loader');
|
||||
const { SourceMap } = require('internal/source_map/source_map');
|
||||
const {
|
||||
constants,
|
||||
enableCompileCache,
|
||||
getCompileCacheDir,
|
||||
} = require('internal/modules/helpers');
|
||||
|
||||
Module.findSourceMap = findSourceMap;
|
||||
Module.register = register;
|
||||
Module.SourceMap = SourceMap;
|
||||
Module.constants = constants;
|
||||
Module.enableCompileCache = enableCompileCache;
|
||||
Module.getCompileCacheDir = getCompileCacheDir;
|
||||
module.exports = Module;
|
||||
|
@ -381,7 +381,7 @@ CompileCacheEnableResult CompileCacheHandler::Enable(Environment* env,
|
||||
cache_dir_with_tag_str))) {
|
||||
result.message = "Skipping compile cache because write permission for " +
|
||||
cache_dir_with_tag_str + " is not granted";
|
||||
result.status = CompileCacheEnableStatus::kFailed;
|
||||
result.status = CompileCacheEnableStatus::FAILED;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -391,7 +391,7 @@ CompileCacheEnableResult CompileCacheHandler::Enable(Environment* env,
|
||||
cache_dir_with_tag_str))) {
|
||||
result.message = "Skipping compile cache because read permission for " +
|
||||
cache_dir_with_tag_str + " is not granted";
|
||||
result.status = CompileCacheEnableStatus::kFailed;
|
||||
result.status = CompileCacheEnableStatus::FAILED;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -406,14 +406,14 @@ CompileCacheEnableResult CompileCacheHandler::Enable(Environment* env,
|
||||
if (err != 0 && err != UV_EEXIST) {
|
||||
result.message =
|
||||
"Cannot create cache directory: " + std::string(uv_strerror(err));
|
||||
result.status = CompileCacheEnableStatus::kFailed;
|
||||
result.status = CompileCacheEnableStatus::FAILED;
|
||||
return result;
|
||||
}
|
||||
|
||||
compile_cache_dir_str_ = absolute_cache_dir_base;
|
||||
result.cache_directory = absolute_cache_dir_base;
|
||||
compile_cache_dir_ = cache_dir_with_tag;
|
||||
result.status = CompileCacheEnableStatus::kEnabled;
|
||||
result.status = CompileCacheEnableStatus::ENABLED;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -36,9 +36,10 @@ struct CompileCacheEntry {
|
||||
};
|
||||
|
||||
#define COMPILE_CACHE_STATUS(V) \
|
||||
V(kFailed) /* Failed to enable the cache */ \
|
||||
V(kEnabled) /* Was not enabled before, and now enabled. */ \
|
||||
V(kAlreadyEnabled) /* Was already enabled. */
|
||||
V(FAILED) /* Failed to enable the cache */ \
|
||||
V(ENABLED) /* Was not enabled before, and now enabled. */ \
|
||||
V(ALREADY_ENABLED) /* Was already enabled. */ \
|
||||
V(DISABLED) /* Has been disabled by NODE_DISABLE_COMPILE_CACHE. */
|
||||
|
||||
enum class CompileCacheEnableStatus : uint8_t {
|
||||
#define V(status) status,
|
||||
|
15
src/env.cc
15
src/env.cc
@ -1123,12 +1123,23 @@ void Environment::InitializeCompileCache() {
|
||||
CompileCacheEnableResult Environment::EnableCompileCache(
|
||||
const std::string& cache_dir) {
|
||||
CompileCacheEnableResult result;
|
||||
std::string disable_env;
|
||||
if (credentials::SafeGetenv(
|
||||
"NODE_DISABLE_COMPILE_CACHE", &disable_env, env_vars())) {
|
||||
result.status = CompileCacheEnableStatus::DISABLED;
|
||||
result.message = "Disabled by NODE_DISABLE_COMPILE_CACHE";
|
||||
Debug(this,
|
||||
DebugCategory::COMPILE_CACHE,
|
||||
"[compile cache] %s.\n",
|
||||
result.message);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!compile_cache_handler_) {
|
||||
std::unique_ptr<CompileCacheHandler> handler =
|
||||
std::make_unique<CompileCacheHandler>(this);
|
||||
result = handler->Enable(this, cache_dir);
|
||||
if (result.status == CompileCacheEnableStatus::kEnabled) {
|
||||
if (result.status == CompileCacheEnableStatus::ENABLED) {
|
||||
compile_cache_handler_ = std::move(handler);
|
||||
AtExit(
|
||||
[](void* env) {
|
||||
@ -1143,7 +1154,7 @@ CompileCacheEnableResult Environment::EnableCompileCache(
|
||||
result.message);
|
||||
}
|
||||
} else {
|
||||
result.status = CompileCacheEnableStatus::kAlreadyEnabled;
|
||||
result.status = CompileCacheEnableStatus::ALREADY_ENABLED;
|
||||
result.cache_directory = compile_cache_handler_->cache_dir();
|
||||
}
|
||||
return result;
|
||||
|
@ -435,6 +435,33 @@ void BindingData::GetPackageScopeConfig(
|
||||
.ToLocalChecked());
|
||||
}
|
||||
|
||||
void EnableCompileCache(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK(args[0]->IsString());
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
Local<Context> context = isolate->GetCurrentContext();
|
||||
Environment* env = Environment::GetCurrent(context);
|
||||
Utf8Value value(isolate, args[0]);
|
||||
CompileCacheEnableResult result = env->EnableCompileCache(*value);
|
||||
std::vector<Local<Value>> values = {
|
||||
v8::Integer::New(isolate, static_cast<uint8_t>(result.status)),
|
||||
ToV8Value(context, result.message).ToLocalChecked(),
|
||||
ToV8Value(context, result.cache_directory).ToLocalChecked()};
|
||||
args.GetReturnValue().Set(Array::New(isolate, values.data(), values.size()));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
args.GetReturnValue().Set(
|
||||
ToV8Value(context, env->compile_cache_handler()->cache_dir())
|
||||
.ToLocalChecked());
|
||||
}
|
||||
|
||||
void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
|
||||
Local<ObjectTemplate> target) {
|
||||
Isolate* isolate = isolate_data->isolate();
|
||||
@ -448,6 +475,8 @@ void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
|
||||
"getNearestParentPackageJSON",
|
||||
GetNearestParentPackageJSON);
|
||||
SetMethod(isolate, target, "getPackageScopeConfig", GetPackageScopeConfig);
|
||||
SetMethod(isolate, target, "enableCompileCache", EnableCompileCache);
|
||||
SetMethod(isolate, target, "getCompileCacheDir", GetCompileCacheDir);
|
||||
}
|
||||
|
||||
void BindingData::CreatePerContextProperties(Local<Object> target,
|
||||
@ -456,6 +485,20 @@ void BindingData::CreatePerContextProperties(Local<Object> target,
|
||||
void* priv) {
|
||||
Realm* realm = Realm::GetCurrent(context);
|
||||
realm->AddBindingData<BindingData>(target);
|
||||
|
||||
std::vector<Local<Value>> compile_cache_status_values;
|
||||
Isolate* isolate = context->GetIsolate();
|
||||
|
||||
#define V(status) \
|
||||
compile_cache_status_values.push_back( \
|
||||
FIXED_ONE_BYTE_STRING(isolate, #status));
|
||||
COMPILE_CACHE_STATUS(V)
|
||||
|
||||
USE(target->Set(context,
|
||||
FIXED_ONE_BYTE_STRING(isolate, "compileCacheStatus"),
|
||||
Array::New(isolate,
|
||||
compile_cache_status_values.data(),
|
||||
compile_cache_status_values.size())));
|
||||
}
|
||||
|
||||
void BindingData::RegisterExternalReferences(
|
||||
@ -464,6 +507,8 @@ void BindingData::RegisterExternalReferences(
|
||||
registry->Register(GetNearestParentPackageJSONType);
|
||||
registry->Register(GetNearestParentPackageJSON);
|
||||
registry->Register(GetPackageScopeConfig);
|
||||
registry->Register(EnableCompileCache);
|
||||
registry->Register(GetCompileCacheDir);
|
||||
}
|
||||
|
||||
} // namespace modules
|
||||
|
21
test/fixtures/compile-cache-wrapper.js
vendored
Normal file
21
test/fixtures/compile-cache-wrapper.js
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
const { enableCompileCache, getCompileCacheDir, constants } = require('module');
|
||||
|
||||
console.log('dir before enableCompileCache:', getCompileCacheDir());
|
||||
const result = enableCompileCache(process.env.NODE_TEST_COMPILE_CACHE_DIR);
|
||||
switch (result.status) {
|
||||
case constants.compileCacheStatus.FAILED:
|
||||
console.log('Compile cache failed. ' + result.message);
|
||||
break;
|
||||
case constants.compileCacheStatus.ENABLED:
|
||||
console.log('Compile cache enabled. ' + result.directory);
|
||||
break;
|
||||
case constants.compileCacheStatus.ALREADY_ENABLED:
|
||||
console.log('Compile cache already enabled.');
|
||||
break;
|
||||
case constants.compileCacheStatus.DISABLED:
|
||||
console.log('Compile cache already disabled.');
|
||||
break;
|
||||
}
|
||||
console.log('dir after enableCompileCache:', getCompileCacheDir());
|
81
test/parallel/test-compile-cache-api-env.js
Normal file
81
test/parallel/test-compile-cache-api-env.js
Normal file
@ -0,0 +1,81 @@
|
||||
'use strict';
|
||||
|
||||
// This tests module.enableCompileCache() and module.getCompileCacheDir() work
|
||||
// with a NODE_COMPILE_CACHE environment variable override.
|
||||
|
||||
require('../common');
|
||||
const { spawnSyncAndAssert } = require('../common/child_process');
|
||||
const assert = require('assert');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const fs = require('fs');
|
||||
|
||||
{
|
||||
// Test that it works with non-existent directory.
|
||||
tmpdir.refresh();
|
||||
const apiDir = tmpdir.resolve('api_dir');
|
||||
const envDir = tmpdir.resolve('env_dir');
|
||||
spawnSyncAndAssert(
|
||||
process.execPath,
|
||||
['-r', fixtures.path('compile-cache-wrapper.js'), fixtures.path('empty.js')],
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_DEBUG_NATIVE: 'COMPILE_CACHE',
|
||||
NODE_COMPILE_CACHE: envDir,
|
||||
NODE_TEST_COMPILE_CACHE_DIR: apiDir,
|
||||
},
|
||||
cwd: tmpdir.path
|
||||
},
|
||||
{
|
||||
stdout(output) {
|
||||
console.log(output); // Logging for debugging.
|
||||
assert.match(output, /dir before enableCompileCache: .*env_dir/);
|
||||
assert.match(output, /Compile cache already enabled/);
|
||||
assert.match(output, /dir after enableCompileCache: .*env_dir/);
|
||||
return true;
|
||||
},
|
||||
stderr(output) {
|
||||
console.log(output); // Logging for debugging.
|
||||
assert.match(output, /reading cache from .*env_dir.* for CommonJS .*empty\.js/);
|
||||
assert.match(output, /empty\.js was not initialized, initializing the in-memory entry/);
|
||||
assert.match(output, /writing cache for .*empty\.js.*success/);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
const cacheDir = fs.readdirSync(tmpdir.path);
|
||||
assert.strictEqual(cacheDir.length, 1);
|
||||
const entries = fs.readdirSync(tmpdir.resolve(cacheDir[0]));
|
||||
assert.strictEqual(entries.length, 1);
|
||||
|
||||
// Second run reads the cache, but no need to re-write because it didn't change.
|
||||
spawnSyncAndAssert(
|
||||
process.execPath,
|
||||
['-r', fixtures.path('compile-cache-wrapper.js'), fixtures.path('empty.js')],
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_DEBUG_NATIVE: 'COMPILE_CACHE',
|
||||
NODE_COMPILE_CACHE: envDir,
|
||||
NODE_TEST_COMPILE_CACHE_DIR: apiDir,
|
||||
},
|
||||
cwd: tmpdir.path
|
||||
},
|
||||
{
|
||||
stdout(output) {
|
||||
console.log(output); // Logging for debugging.
|
||||
assert.match(output, /dir before enableCompileCache: .*env_dir/);
|
||||
assert.match(output, /Compile cache already enabled/);
|
||||
assert.match(output, /dir after enableCompileCache: .*env_dir/);
|
||||
return true;
|
||||
},
|
||||
stderr(output) {
|
||||
console.log(output); // Logging for debugging.
|
||||
assert.match(output, /reading cache from .*env_dir.* for CommonJS .*empty\.js/);
|
||||
assert.match(output, /cache for .*empty\.js was accepted, keeping the in-memory entry/);
|
||||
assert.match(output, /.*skip .*empty\.js because cache was the same/);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
56
test/parallel/test-compile-cache-api-permission.js
Normal file
56
test/parallel/test-compile-cache-api-permission.js
Normal file
@ -0,0 +1,56 @@
|
||||
'use strict';
|
||||
|
||||
// This tests module.enableCompileCache() works with the permission model.
|
||||
|
||||
require('../common');
|
||||
const { spawnSyncAndAssert } = require('../common/child_process');
|
||||
const assert = require('assert');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const fs = require('fs');
|
||||
|
||||
{
|
||||
tmpdir.refresh();
|
||||
const cacheDir = tmpdir.resolve('compile-cache');
|
||||
const scriptDir = tmpdir.resolve('scripts');
|
||||
// If the directory doesn't exist, permission will just be disallowed.
|
||||
fs.mkdirSync(cacheDir);
|
||||
fs.mkdirSync(scriptDir);
|
||||
|
||||
const empty = tmpdir.resolve('scripts', 'empty.js');
|
||||
const wrapper = tmpdir.resolve('scripts', 'compile-cache-wrapper.js');
|
||||
|
||||
fs.copyFileSync(fixtures.path('empty.js'), empty);
|
||||
fs.copyFileSync(fixtures.path('compile-cache-wrapper.js'), wrapper);
|
||||
|
||||
spawnSyncAndAssert(
|
||||
process.execPath,
|
||||
[
|
||||
'--experimental-permission', `--allow-fs-read=${scriptDir}`, `--allow-fs-write=${scriptDir}`,
|
||||
'-r', wrapper, empty,
|
||||
],
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_DEBUG_NATIVE: 'COMPILE_CACHE',
|
||||
NODE_COMPILE_CACHE: undefined,
|
||||
NODE_TEST_COMPILE_CACHE_DIR: cacheDir,
|
||||
},
|
||||
cwd: tmpdir.path
|
||||
},
|
||||
{
|
||||
stdout(output) {
|
||||
console.log(output); // Logging for debugging.
|
||||
assert.match(output, /dir before enableCompileCache: undefined/);
|
||||
assert.match(output, /Compile cache failed/);
|
||||
assert.match(output, /Skipping compile cache because write permission for .* is not granted/);
|
||||
assert.match(output, /dir after enableCompileCache: undefined/);
|
||||
return true;
|
||||
},
|
||||
stderr(output) {
|
||||
console.log(output); // Logging for debugging.
|
||||
assert.match(output, /Skipping compile cache because write permission for .* is not granted/);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
79
test/parallel/test-compile-cache-api-success.js
Normal file
79
test/parallel/test-compile-cache-api-success.js
Normal file
@ -0,0 +1,79 @@
|
||||
'use strict';
|
||||
|
||||
// This tests module.enableCompileCache() and module.getCompileCacheDir() work.
|
||||
|
||||
require('../common');
|
||||
const { spawnSyncAndAssert } = require('../common/child_process');
|
||||
const assert = require('assert');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
{
|
||||
// Test that it works with non-existent directory.
|
||||
tmpdir.refresh();
|
||||
const dir = tmpdir.resolve('.compile_cache_dir');
|
||||
|
||||
spawnSyncAndAssert(
|
||||
process.execPath,
|
||||
['-r', fixtures.path('compile-cache-wrapper.js'), fixtures.path('snapshot', 'typescript.js')],
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_DEBUG_NATIVE: 'COMPILE_CACHE',
|
||||
NODE_COMPILE_CACHE: undefined,
|
||||
NODE_TEST_COMPILE_CACHE_DIR: dir,
|
||||
},
|
||||
cwd: tmpdir.path
|
||||
},
|
||||
{
|
||||
stdout(output) {
|
||||
console.log(output); // Logging for debugging.
|
||||
assert.match(output, /dir before enableCompileCache: undefined/);
|
||||
assert.match(output, /Compile cache enabled\. .*\.compile_cache_dir/);
|
||||
assert.match(output, /dir after enableCompileCache: .*\.compile_cache_dir/);
|
||||
return true;
|
||||
},
|
||||
stderr(output) {
|
||||
console.log(output); // Logging for debugging.
|
||||
assert.match(output, /typescript\.js was not initialized, initializing the in-memory entry/);
|
||||
assert.match(output, /writing cache for .*typescript\.js.*success/);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
const cacheDir = fs.readdirSync(dir);
|
||||
assert.strictEqual(cacheDir.length, 1);
|
||||
const entries = fs.readdirSync(path.join(dir, cacheDir[0]));
|
||||
assert.strictEqual(entries.length, 1);
|
||||
|
||||
// Second run reads the cache, but no need to re-write because it didn't change.
|
||||
spawnSyncAndAssert(
|
||||
process.execPath,
|
||||
['-r', fixtures.path('compile-cache-wrapper.js'), fixtures.path('snapshot', 'typescript.js')],
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_DEBUG_NATIVE: 'COMPILE_CACHE',
|
||||
NODE_COMPILE_CACHE: undefined,
|
||||
NODE_TEST_COMPILE_CACHE_DIR: dir,
|
||||
},
|
||||
cwd: tmpdir.path
|
||||
},
|
||||
{
|
||||
stdout(output) {
|
||||
console.log(output); // Logging for debugging.
|
||||
assert.match(output, /dir before enableCompileCache: undefined/);
|
||||
assert.match(output, /Compile cache enabled\. .*\.compile_cache_dir/);
|
||||
assert.match(output, /dir after enableCompileCache: .*\.compile_cache_dir/);
|
||||
return true;
|
||||
},
|
||||
stderr(output) {
|
||||
console.log(output); // Logging for debugging.
|
||||
assert.match(output, /cache for .*typescript\.js was accepted, keeping the in-memory entry/);
|
||||
assert.match(output, /.*skip .*typescript\.js because cache was the same/);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
92
test/parallel/test-compile-cache-api-tmpdir.js
Normal file
92
test/parallel/test-compile-cache-api-tmpdir.js
Normal file
@ -0,0 +1,92 @@
|
||||
'use strict';
|
||||
|
||||
// This tests module.enableCompileCache() and module.getCompileCacheDir() work with
|
||||
// the TMPDIR environemnt variable override.
|
||||
|
||||
require('../common');
|
||||
const { spawnSyncAndAssert } = require('../common/child_process');
|
||||
const assert = require('assert');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
{
|
||||
// Test that it works with non-existent directory.
|
||||
tmpdir.refresh();
|
||||
|
||||
spawnSyncAndAssert(
|
||||
process.execPath,
|
||||
['-r', fixtures.path('compile-cache-wrapper.js'), fixtures.path('empty.js')],
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_DEBUG_NATIVE: 'COMPILE_CACHE',
|
||||
NODE_COMPILE_CACHE: undefined,
|
||||
NODE_TEST_COMPILE_CACHE_DIR: undefined,
|
||||
// TMPDIR is ignored on Windows while on other platforms, TMPDIR takes precedence.
|
||||
// Override all related environment variables to ensure the tmpdir is configured properly
|
||||
// regardless of the global environment variables used to run the test.
|
||||
TMPDIR: tmpdir.path,
|
||||
TEMP: tmpdir.path,
|
||||
TMP: tmpdir.path,
|
||||
},
|
||||
cwd: tmpdir.path
|
||||
},
|
||||
{
|
||||
stdout(output) {
|
||||
console.log(output); // Logging for debugging.
|
||||
assert.match(output, /dir before enableCompileCache: undefined/);
|
||||
assert.match(output, /Compile cache enabled\. .*node-compile-cache/);
|
||||
assert.match(output, /dir after enableCompileCache: .*node-compile-cache/);
|
||||
return true;
|
||||
},
|
||||
stderr(output) {
|
||||
console.log(output); // Logging for debugging.
|
||||
assert.match(output, /empty\.js was not initialized, initializing the in-memory entry/);
|
||||
assert.match(output, /writing cache for .*empty\.js.*success/);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
const baseDir = path.join(tmpdir.path, 'node-compile-cache');
|
||||
const cacheDir = fs.readdirSync(baseDir);
|
||||
assert.strictEqual(cacheDir.length, 1);
|
||||
const entries = fs.readdirSync(path.join(baseDir, cacheDir[0]));
|
||||
assert.strictEqual(entries.length, 1);
|
||||
|
||||
// Second run reads the cache, but no need to re-write because it didn't change.
|
||||
spawnSyncAndAssert(
|
||||
process.execPath,
|
||||
['-r', fixtures.path('compile-cache-wrapper.js'), fixtures.path('empty.js')],
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_DEBUG_NATIVE: 'COMPILE_CACHE',
|
||||
NODE_COMPILE_CACHE: undefined,
|
||||
NODE_TEST_COMPILE_CACHE_DIR: undefined,
|
||||
// TMPDIR is ignored on Windows while on other platforms, TMPDIR takes precedence.
|
||||
// Override all related environment variables to ensure the tmpdir is configured properly
|
||||
// regardless of the global environment variables used to run the test.
|
||||
TMPDIR: tmpdir.path,
|
||||
TEMP: tmpdir.path,
|
||||
TMP: tmpdir.path,
|
||||
},
|
||||
cwd: tmpdir.path
|
||||
},
|
||||
{
|
||||
stdout(output) {
|
||||
console.log(output); // Logging for debugging.
|
||||
assert.match(output, /dir before enableCompileCache: undefined/);
|
||||
assert.match(output, /Compile cache enabled\. .*node-compile-cache/);
|
||||
assert.match(output, /dir after enableCompileCache: .*node-compile-cache/);
|
||||
return true;
|
||||
},
|
||||
stderr(output) {
|
||||
console.log(output); // Logging for debugging.
|
||||
assert.match(output, /cache for .*empty\.js was accepted, keeping the in-memory entry/);
|
||||
assert.match(output, /.*skip .*empty\.js because cache was the same/);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
39
test/parallel/test-compile-cache-disable.js
Normal file
39
test/parallel/test-compile-cache-disable.js
Normal file
@ -0,0 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const { spawnSyncAndAssert } = require('../common/child_process');
|
||||
const assert = require('assert');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
{
|
||||
tmpdir.refresh();
|
||||
|
||||
spawnSyncAndAssert(
|
||||
process.execPath,
|
||||
['-r', fixtures.path('compile-cache-wrapper.js'), fixtures.path('empty.js')],
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_DEBUG_NATIVE: 'COMPILE_CACHE',
|
||||
NODE_COMPILE_CACHE: undefined,
|
||||
NODE_TEST_COMPILE_CACHE_DIR: tmpdir.path,
|
||||
NODE_DISABLE_COMPILE_CACHE: '1',
|
||||
},
|
||||
cwd: tmpdir.path
|
||||
},
|
||||
{
|
||||
stdout(output) {
|
||||
console.log(output); // Logging for debugging.
|
||||
assert.match(output, /dir before enableCompileCache: undefined/);
|
||||
assert.match(output, /Compile cache already disabled/);
|
||||
assert.match(output, /dir after enableCompileCache: undefined/);
|
||||
return true;
|
||||
},
|
||||
stderr(output) {
|
||||
console.log(output); // Logging for debugging.
|
||||
assert.match(output, /Disabled by NODE_DISABLE_COMPILE_CACHE/);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user