diff --git a/doc/api/module.md b/doc/api/module.md
index f05726286f09..f3707765a381 100644
--- a/doc/api/module.md
+++ b/doc/api/module.md
@@ -464,10 +464,12 @@ no-op, and the directory is not created if it is missing.
### Limitations of the compile cache
-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.
+V8 cannot collect block-level [V8 JavaScript code coverage][] for functions that
+are deserialized from the code cache. The compile cache is therefore disabled
+when coverage collection starts with the process, through
+[`NODE_V8_COVERAGE=dir`][] or [`--experimental-test-coverage`][]. Coverage that
+is started later, for example through the inspector, may be less precise in
+functions that were already deserialized from the code cache.
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
@@ -525,7 +527,8 @@ The following constants are returned as the `status` field in the object returne
DISABLED |
Node.js cannot enable the compile cache because the environment variable
- NODE_DISABLE_COMPILE_CACHE=1 has been set.
+ NODE_DISABLE_COMPILE_CACHE=1 has been set, or because V8
+ coverage is being collected.
|
@@ -2111,6 +2114,7 @@ returned object contains the following keys:
[V8 code cache]: https://v8.dev/blog/code-caching-for-devs
[`"exports"`]: packages.md#exports
[`--enable-source-maps`]: cli.md#--enable-source-maps
+[`--experimental-test-coverage`]: cli.md#--experimental-test-coverage
[`--import`]: cli.md#--importmodule
[`--require`]: cli.md#-r---require-module
[`NODE_COMPILE_CACHE=dir`]: cli.md#node_compile_cachedir
diff --git a/src/compile_cache.h b/src/compile_cache.h
index 527f72f33d42..25071de4ccea 100644
--- a/src/compile_cache.h
+++ b/src/compile_cache.h
@@ -47,7 +47,7 @@ struct CompileCacheEntry {
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. */
+ V(DISABLED) /* Disabled by the environment or by V8 coverage. */
enum class CompileCacheEnableStatus : uint8_t {
#define V(status) status,
diff --git a/src/env.cc b/src/env.cc
index c30b6e2f5049..342fec1a8317 100644
--- a/src/env.cc
+++ b/src/env.cc
@@ -1396,10 +1396,20 @@ CompileCacheEnableResult Environment::EnableCompileCache(
const std::string& cache_dir, EnableOption option) {
CompileCacheEnableResult result;
std::string disable_env;
+ const char* disabled_reason = nullptr;
if (credentials::SafeGetenv(
"NODE_DISABLE_COMPILE_CACHE", &disable_env, this)) {
+ disabled_reason = "Disabled by NODE_DISABLE_COMPILE_CACHE";
+#if HAVE_INSPECTOR
+ } else if (coverage_connection() != nullptr) {
+ // V8 cannot collect block coverage for functions deserialized from the
+ // cache.
+ disabled_reason = "Disabled because V8 coverage is being collected";
+#endif // HAVE_INSPECTOR
+ }
+ if (disabled_reason != nullptr) {
result.status = CompileCacheEnableStatus::DISABLED;
- result.message = "Disabled by NODE_DISABLE_COMPILE_CACHE";
+ result.message = disabled_reason;
Debug(this,
DebugCategory::COMPILE_CACHE,
"[compile cache] %s.\n",
diff --git a/test/common/compile-cache.js b/test/common/compile-cache.js
new file mode 100644
index 000000000000..4a96856b2fd0
--- /dev/null
+++ b/test/common/compile-cache.js
@@ -0,0 +1,24 @@
+'use strict';
+
+const common = require('./');
+const childProcess = require('./child_process');
+
+// The compile cache is disabled while V8 coverage is being collected. In that
+// case, check that the first child says so and skip the rest of the test.
+function withCompileCache(spawn) {
+ return (...args) => {
+ if (!process.env.NODE_V8_COVERAGE) {
+ return spawn(...args);
+ }
+ args.pop();
+ childProcess.spawnSyncAndExit(...args, {
+ stderr: /Disabled because V8 coverage is being collected/,
+ });
+ common.skip('the compile cache is disabled by V8 coverage');
+ };
+}
+
+module.exports = {
+ spawnSyncAndAssert: withCompileCache(childProcess.spawnSyncAndAssert),
+ spawnSyncAndExit: withCompileCache(childProcess.spawnSyncAndExit),
+};
diff --git a/test/parallel/test-compile-cache-api-env.js b/test/parallel/test-compile-cache-api-env.js
index 197f1495a886..7504d7a1fd60 100644
--- a/test/parallel/test-compile-cache-api-env.js
+++ b/test/parallel/test-compile-cache-api-env.js
@@ -4,7 +4,7 @@
// with a NODE_COMPILE_CACHE environment variable override.
require('../common');
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
diff --git a/test/parallel/test-compile-cache-api-flush.js b/test/parallel/test-compile-cache-api-flush.js
index 7e66b1fee103..fb4c26ba1bb0 100644
--- a/test/parallel/test-compile-cache-api-flush.js
+++ b/test/parallel/test-compile-cache-api-flush.js
@@ -3,7 +3,7 @@
// This tests module.flushCompileCache() works as expected.
require('../common');
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const tmpdir = require('../common/tmpdir');
const fixtures = require('../common/fixtures');
diff --git a/test/parallel/test-compile-cache-api-options-portable-env.js b/test/parallel/test-compile-cache-api-options-portable-env.js
index 5586e3e84046..d9f196d33045 100644
--- a/test/parallel/test-compile-cache-api-options-portable-env.js
+++ b/test/parallel/test-compile-cache-api-options-portable-env.js
@@ -4,7 +4,7 @@
// variable NODE_COMPILE_CACHE overrides.
require('../common');
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
diff --git a/test/parallel/test-compile-cache-api-portable.js b/test/parallel/test-compile-cache-api-portable.js
index 620b3bdce875..3ba390503c40 100644
--- a/test/parallel/test-compile-cache-api-portable.js
+++ b/test/parallel/test-compile-cache-api-portable.js
@@ -4,7 +4,7 @@
// and supports portable paths across directory relocations.
require('../common');
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const fs = require('fs');
const tmpdir = require('../common/tmpdir');
diff --git a/test/parallel/test-compile-cache-api-readonly.js b/test/parallel/test-compile-cache-api-readonly.js
index 8ac0248a4d53..df91ee45e4b9 100644
--- a/test/parallel/test-compile-cache-api-readonly.js
+++ b/test/parallel/test-compile-cache-api-readonly.js
@@ -4,7 +4,7 @@
// entries are read, nothing is written, and a missing directory is not created.
const common = require('../common');
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const fs = require('fs');
const path = require('path');
diff --git a/test/parallel/test-compile-cache-api-success.js b/test/parallel/test-compile-cache-api-success.js
index 2d4d94fd6e9d..e8a5226f7b50 100644
--- a/test/parallel/test-compile-cache-api-success.js
+++ b/test/parallel/test-compile-cache-api-success.js
@@ -3,7 +3,7 @@
// This tests module.enableCompileCache() and module.getCompileCacheDir() work.
require('../common');
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
diff --git a/test/parallel/test-compile-cache-api-tmpdir.js b/test/parallel/test-compile-cache-api-tmpdir.js
index e676893b01f5..1d4cdbb242c7 100644
--- a/test/parallel/test-compile-cache-api-tmpdir.js
+++ b/test/parallel/test-compile-cache-api-tmpdir.js
@@ -4,7 +4,7 @@
// the TMPDIR environment variable override.
require('../common');
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
diff --git a/test/parallel/test-compile-cache-bad-syntax.js b/test/parallel/test-compile-cache-bad-syntax.js
index 63f6374b2c38..b868ad385e9d 100644
--- a/test/parallel/test-compile-cache-bad-syntax.js
+++ b/test/parallel/test-compile-cache-bad-syntax.js
@@ -3,7 +3,7 @@
// This tests NODE_COMPILE_CACHE works.
require('../common');
-const { spawnSyncAndExit } = require('../common/child_process');
+const { spawnSyncAndExit } = require('../common/compile-cache');
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const assert = require('assert');
diff --git a/test/parallel/test-compile-cache-coverage.js b/test/parallel/test-compile-cache-coverage.js
new file mode 100644
index 000000000000..f2f5e9e2f77f
--- /dev/null
+++ b/test/parallel/test-compile-cache-coverage.js
@@ -0,0 +1,45 @@
+'use strict';
+
+const common = require('../common');
+common.skipIfInspectorDisabled();
+
+const { spawnSyncAndAssert } = require('../common/child_process');
+const assert = require('assert');
+const fixtures = require('../common/fixtures');
+const fs = require('fs');
+const tmpdir = require('../common/tmpdir');
+
+tmpdir.refresh();
+const cacheDir = tmpdir.resolve('compile_cache');
+const env = {
+ ...process.env,
+ NODE_DEBUG_NATIVE: 'COMPILE_CACHE',
+ NODE_V8_COVERAGE: tmpdir.resolve('coverage'),
+};
+
+spawnSyncAndAssert(
+ process.execPath,
+ [fixtures.path('empty.js')],
+ { env: { ...env, NODE_COMPILE_CACHE: cacheDir }, cwd: tmpdir.path },
+ { stderr: /Disabled because V8 coverage is being collected/ });
+assert(!fs.existsSync(cacheDir));
+
+spawnSyncAndAssert(
+ process.execPath,
+ ['-r', fixtures.path('compile-cache-wrapper.js'), fixtures.path('empty.js')],
+ {
+ env: {
+ ...env,
+ NODE_COMPILE_CACHE: undefined,
+ NODE_TEST_COMPILE_CACHE_DIR: cacheDir,
+ },
+ cwd: tmpdir.path,
+ },
+ {
+ stdout(output) {
+ assert.match(output, /Compile cache already disabled/);
+ assert.match(output, /dir after enableCompileCache: undefined/);
+ },
+ stderr: /Disabled because V8 coverage is being collected/,
+ });
+assert(!fs.existsSync(cacheDir));
diff --git a/test/parallel/test-compile-cache-dynamic-import.js b/test/parallel/test-compile-cache-dynamic-import.js
index 01731dc68225..757e992eaa88 100644
--- a/test/parallel/test-compile-cache-dynamic-import.js
+++ b/test/parallel/test-compile-cache-dynamic-import.js
@@ -3,7 +3,7 @@
// This tests NODE_COMPILE_CACHE works in existing directory.
require('../common');
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const tmpdir = require('../common/tmpdir');
const fixtures = require('../common/fixtures');
diff --git a/test/parallel/test-compile-cache-esm.js b/test/parallel/test-compile-cache-esm.js
index 963de8bcaba2..9bddc23424c2 100644
--- a/test/parallel/test-compile-cache-esm.js
+++ b/test/parallel/test-compile-cache-esm.js
@@ -3,7 +3,7 @@
// This tests NODE_COMPILE_CACHE works in existing directory.
require('../common');
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const tmpdir = require('../common/tmpdir');
const fixtures = require('../common/fixtures');
diff --git a/test/parallel/test-compile-cache-existing-directory.js b/test/parallel/test-compile-cache-existing-directory.js
index 0fc60f9544d5..d9c7e690fd50 100644
--- a/test/parallel/test-compile-cache-existing-directory.js
+++ b/test/parallel/test-compile-cache-existing-directory.js
@@ -3,7 +3,7 @@
// This tests NODE_COMPILE_CACHE works in existing directory.
require('../common');
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const fs = require('fs');
const fixtures = require('../common/fixtures');
diff --git a/test/parallel/test-compile-cache-portable-esm.js b/test/parallel/test-compile-cache-portable-esm.js
index 18766b6810ea..bfcb6f9e2da5 100644
--- a/test/parallel/test-compile-cache-portable-esm.js
+++ b/test/parallel/test-compile-cache-portable-esm.js
@@ -3,7 +3,7 @@
// This tests NODE_COMPILE_CACHE works after moving directory and unusual characters in path are handled correctly.
require('../common');
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const tmpdir = require('../common/tmpdir');
const fs = require('fs');
diff --git a/test/parallel/test-compile-cache-portable.js b/test/parallel/test-compile-cache-portable.js
index b5978b5b0c62..04b7360833ca 100644
--- a/test/parallel/test-compile-cache-portable.js
+++ b/test/parallel/test-compile-cache-portable.js
@@ -4,7 +4,7 @@
// environment variable.
require('../common');
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const fs = require('fs');
const tmpdir = require('../common/tmpdir');
diff --git a/test/parallel/test-compile-cache-success.js b/test/parallel/test-compile-cache-success.js
index c02a62432869..4d5d7a67214a 100644
--- a/test/parallel/test-compile-cache-success.js
+++ b/test/parallel/test-compile-cache-success.js
@@ -3,7 +3,7 @@
// This tests NODE_COMPILE_CACHE works.
require('../common');
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
diff --git a/test/parallel/test-compile-cache-typescript-commonjs.js b/test/parallel/test-compile-cache-typescript-commonjs.js
index 60e86c3c0f9d..5ee65a806e67 100644
--- a/test/parallel/test-compile-cache-typescript-commonjs.js
+++ b/test/parallel/test-compile-cache-typescript-commonjs.js
@@ -6,7 +6,7 @@ const common = require('../common');
if (!process.config.variables.node_use_amaro) {
common.skip('Requires Amaro');
}
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const tmpdir = require('../common/tmpdir');
const fixtures = require('../common/fixtures');
diff --git a/test/parallel/test-compile-cache-typescript-esm.js b/test/parallel/test-compile-cache-typescript-esm.js
index 98473dbb2a5b..e85f442e5ac1 100644
--- a/test/parallel/test-compile-cache-typescript-esm.js
+++ b/test/parallel/test-compile-cache-typescript-esm.js
@@ -6,7 +6,7 @@ const common = require('../common');
if (!process.config.variables.node_use_amaro) {
common.skip('Requires Amaro');
}
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const tmpdir = require('../common/tmpdir');
const fixtures = require('../common/fixtures');
diff --git a/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js b/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js
index a6c95e432b78..f229587c1f26 100644
--- a/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js
+++ b/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js
@@ -7,7 +7,7 @@ const common = require('../common');
if (!process.config.variables.node_use_amaro) {
common.skip('Requires Amaro');
}
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const tmpdir = require('../common/tmpdir');
const fixtures = require('../common/fixtures');
diff --git a/test/parallel/test-compile-cache-updated-file.js b/test/parallel/test-compile-cache-updated-file.js
index 68f20a6990cc..c3fecae97f0f 100644
--- a/test/parallel/test-compile-cache-updated-file.js
+++ b/test/parallel/test-compile-cache-updated-file.js
@@ -3,7 +3,7 @@
// This tests NODE_COMPILE_CACHE works in existing directory.
require('../common');
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const fs = require('fs');
const tmpdir = require('../common/tmpdir');
diff --git a/test/parallel/test-compile-cache-wasm.js b/test/parallel/test-compile-cache-wasm.js
index 278198378dd2..31019b33892f 100644
--- a/test/parallel/test-compile-cache-wasm.js
+++ b/test/parallel/test-compile-cache-wasm.js
@@ -3,7 +3,7 @@
// This tests NODE_COMPILE_CACHE works for WebAssembly modules imported as ESM.
require('../common');
-const { spawnSyncAndAssert } = require('../common/child_process');
+const { spawnSyncAndAssert } = require('../common/compile-cache');
const assert = require('assert');
const tmpdir = require('../common/tmpdir');
const fixtures = require('../common/fixtures');