| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 1dd744a commit 8d76db8
16 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -135,26 +135,22 @@ import { findSourceMap, SourceMap } from 'module'; | |||
| 135 | 135 | const { findSourceMap, SourceMap } = require('module'); | |
| 136 | 136 | ``` | |
| 137 | 137 | ||
| 138 | - ### `module.findSourceMap(path[, error])` | ||
| 138 | + <!-- Anchors to make sure old links find a target --> | ||
| 139 | + <a id="module_module_findsourcemap_path_error"></a> | ||
| 140 | + ### `module.findSourceMap(path)` | ||
| 141 | + | ||
| 139 | 142 | <!-- YAML | |
| 140 | 143 | added: | |
| 141 | 144 | - v13.7.0 | |
| 142 | 145 | - v12.17.0 | |
| 143 | 146 | --> | |
| 144 | 147 | ||
| 145 | 148 | * `path` {string} | |
| 146 | - * `error` {Error} | ||
| 147 | 149 | * Returns: {module.SourceMap} | |
| 148 | 150 | ||
| 149 | 151 | `path` is the resolved path for the file for which a corresponding source map | |
| 150 | 152 | should be fetched. | |
| 151 | 153 | ||
| 152 | - The `error` instance should be passed as the second parameter to `findSourceMap` | ||
| 153 | - in exceptional flows, such as when an overridden | ||
| 154 | - [`Error.prepareStackTrace(error, trace)`][] is invoked. Modules are not added to | ||
| 155 | - the module cache until they are successfully loaded. In these cases, source maps | ||
| 156 | - are associated with the `error` instance along with the `path`. | ||
| 157 | - | ||
| 158 | 154 | ### Class: `module.SourceMap` | |
| 159 | 155 | <!-- YAML | |
| 160 | 156 | added: | |
@@ -204,7 +200,6 @@ consists of the following keys: | |||
| 204 | 200 | [ES Modules]: esm.md | |
| 205 | 201 | [Source map v3 format]: https://sourcemaps.info/spec.html#h.mofvlxcwqzej | |
| 206 | 202 | [`--enable-source-maps`]: cli.md#cli_enable_source_maps | |
| 207 | - [`Error.prepareStackTrace(error, trace)`]: https://v8.dev/docs/stack-trace-api#customizing-stack-traces | ||
| 208 | 203 | [`NODE_V8_COVERAGE=dir`]: cli.md#cli_node_v8_coverage_dir | |
| 209 | 204 | [`SourceMap`]: #module_class_module_sourcemap | |
| 210 | 205 | [`createRequire()`]: #module_module_createrequire_filename | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -967,7 +967,7 @@ This section was moved to | |||
| 967 | 967 | [Modules: `module` core module](module.md#module_source_map_v3_support). | |
| 968 | 968 | ||
| 969 | 969 | <!-- Anchors to make sure old links find a target --> | |
| 970 | - * <a id="modules_module_findsourcemap_path_error" href="module.html#module_module_findsourcemap_path_error">`module.findSourceMap(path[, error])`</a> | ||
| 970 | + * <a id="modules_module_findsourcemap_path_error" href="module.html#module_module_findsourcemap_path">`module.findSourceMap(path)`</a> | ||
| 971 | 971 | * <a id="modules_class_module_sourcemap" href="module.html#module_class_module_sourcemap">Class: `module.SourceMap`</a> | |
| 972 | 972 | * <a id="modules_new_sourcemap_payload" href="module.html#module_new_sourcemap_payload">`new SourceMap(payload)`</a> | |
| 973 | 973 | * <a id="modules_sourcemap_payload" href="module.html#module_sourcemap_payload">`sourceMap.payload`</a> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -86,6 +86,7 @@ function makeSafe(unsafe, safe) { | |||
| 86 | 86 | Object.freeze(safe); | |
| 87 | 87 | return safe; | |
| 88 | 88 | } | |
| 89 | + primordials.makeSafe = makeSafe; | ||
| 89 | 90 | ||
| 90 | 91 | // Subclass the constructors because we need to use their prototype | |
| 91 | 92 | // methods later. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -57,7 +57,7 @@ const prepareStackTrace = (globalThis, error, trace) => { | |||
| 57 | 57 | let str = i !== 0 ? '\n at ' : ''; | |
| 58 | 58 | str = `${str}${t}`; | |
| 59 | 59 | try { | |
| 60 | - const sm = findSourceMap(t.getFileName(), error); | ||
| 60 | + const sm = findSourceMap(t.getFileName()); | ||
| 61 | 61 | if (sm) { | |
| 62 | 62 | // Source Map V3 lines/columns use zero-based offsets whereas, in | |
| 63 | 63 | // stack traces, they start at 1/1. | |
@@ -119,6 +119,7 @@ function getErrorSource(payload, originalSource, firstLine, firstColumn) { | |||
| 119 | 119 | source = readFileSync(originalSourceNoScheme, 'utf8'); | |
| 120 | 120 | } catch (err) { | |
| 121 | 121 | debug(err); | |
| 122 | + return ''; | ||
| 122 | 123 | } | |
| 123 | 124 | } | |
| 124 | 125 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,15 +1,18 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | 2 | ||
| 3 | 3 | const { | |
| 4 | + ArrayPrototypeMap, | ||
| 4 | 5 | JSONParse, | |
| 5 | 6 | ObjectCreate, | |
| 6 | 7 | ObjectKeys, | |
| 7 | 8 | ObjectGetOwnPropertyDescriptor, | |
| 8 | 9 | ObjectPrototypeHasOwnProperty, | |
| 9 | 10 | Map, | |
| 10 | 11 | MapPrototypeEntries, | |
| 11 | - WeakMap, | ||
| 12 | - WeakMapPrototypeGet, | ||
| 12 | + RegExpPrototypeTest, | ||
| 13 | + SafeMap, | ||
| 14 | + StringPrototypeMatch, | ||
| 15 | + StringPrototypeSplit, | ||
| 13 | 16 | uncurryThis, | |
| 14 | 17 | } = primordials; | |
| 15 | 18 | ||
@@ -27,17 +30,17 @@ let debug = require('internal/util/debuglog').debuglog('source_map', (fn) => { | |||
| 27 | 30 | }); | |
| 28 | 31 | const fs = require('fs'); | |
| 29 | 32 | const { getOptionValue } = require('internal/options'); | |
| 33 | + const { IterableWeakMap } = require('internal/util/iterable_weak_map'); | ||
| 30 | 34 | const { | |
| 31 | 35 | normalizeReferrerURL, | |
| 32 | 36 | } = require('internal/modules/cjs/helpers'); | |
| 33 | - // For cjs, since Module._cache is exposed to users, we use a WeakMap | ||
| 34 | - // keyed on module, facilitating garbage collection. | ||
| 35 | - const cjsSourceMapCache = new WeakMap(); | ||
| 36 | - // The esm cache is not exposed to users, so we can use a Map keyed | ||
| 37 | - // on filenames. | ||
| 38 | - const esmSourceMapCache = new Map(); | ||
| 39 | - const { fileURLToPath, URL } = require('url'); | ||
| 40 | - let Module; | ||
| 37 | + // Since the CJS module cache is mutable, which leads to memory leaks when | ||
| 38 | + // modules are deleted, we use a WeakMap so that the source map cache will | ||
| 39 | + // be purged automatically: | ||
| 40 | + const cjsSourceMapCache = new IterableWeakMap(); | ||
| 41 | + // The esm cache is not mutable, so we can use a Map without memory concerns: | ||
| 42 | + const esmSourceMapCache = new SafeMap(); | ||
| 43 | + const { fileURLToPath, pathToFileURL, URL } = require('internal/url'); | ||
| 41 | 44 | let SourceMap; | |
| 42 | 45 | ||
| 43 | 46 | let sourceMapsEnabled; | |
@@ -70,13 +73,14 @@ function maybeCacheSourceMap(filename, content, cjsModuleInstance) { | |||
| 70 | 73 | debug(err.stack); | |
| 71 | 74 | return; | |
| 72 | 75 | } | |
| 73 | - | ||
| 74 | - const match = content.match(/\/[*/]#\s+sourceMappingURL=(?<sourceMappingURL>[^\s]+)/); | ||
| 76 | + const match = StringPrototypeMatch( | ||
| 77 | + content, | ||
| 78 | + /\/[*/]#\s+sourceMappingURL=(?<sourceMappingURL>[^\s]+)/ | ||
| 79 | + ); | ||
| 75 | 80 | if (match) { | |
| 76 | 81 | const data = dataFromUrl(filename, match.groups.sourceMappingURL); | |
| 77 | 82 | const url = data ? null : match.groups.sourceMappingURL; | |
| 78 | 83 | if (cjsModuleInstance) { | |
| 79 | - if (!Module) Module = require('internal/modules/cjs/loader').Module; | ||
| 80 | 84 | cjsSourceMapCache.set(cjsModuleInstance, { | |
| 81 | 85 | filename, | |
| 82 | 86 | lineLengths: lineLengths(content), | |
@@ -120,7 +124,7 @@ function lineLengths(content) { | |||
| 120 | 124 | // We purposefully keep \r as part of the line-length calculation, in | |
| 121 | 125 | // cases where there is a \r\n separator, so that this can be taken into | |
| 122 | 126 | // account in coverage calculations. | |
| 123 | - return content.split(/\n|\u2028|\u2029/).map((line) => { | ||
| 127 | + return ArrayPrototypeMap(StringPrototypeSplit(content, /\n|\u2028|\u2029/), (line) => { | ||
| 124 | 128 | return line.length; | |
| 125 | 129 | }); | |
| 126 | 130 | } | |
@@ -139,8 +143,8 @@ function sourceMapFromFile(mapURL) { | |||
| 139 | 143 | // data:[<mediatype>][;base64],<data> see: | |
| 140 | 144 | // https://tools.ietf.org/html/rfc2397#section-2 | |
| 141 | 145 | function sourceMapFromDataUrl(sourceURL, url) { | |
| 142 | - const [format, data] = url.split(','); | ||
| 143 | - const splitFormat = format.split(';'); | ||
| 146 | + const [format, data] = StringPrototypeSplit(url, ','); | ||
| 147 | + const splitFormat = StringPrototypeSplit(format, ';'); | ||
| 144 | 148 | const contentType = splitFormat[0]; | |
| 145 | 149 | const base64 = splitFormat[splitFormat.length - 1] === 'base64'; | |
| 146 | 150 | if (contentType === 'application/json') { | |
@@ -207,48 +211,32 @@ function sourceMapCacheToObject() { | |||
| 207 | 211 | return obj; | |
| 208 | 212 | } | |
| 209 | 213 | ||
| 210 | - // Since WeakMap can't be iterated over, we use Module._cache's | ||
| 211 | - // keys to facilitate Source Map serialization. | ||
| 212 | - // | ||
| 213 | - // TODO(bcoe): this means we don't currently serialize source-maps attached | ||
| 214 | - // to error instances, only module instances. | ||
| 215 | 214 | function appendCJSCache(obj) { | |
| 216 | - if (!Module) return; | ||
| 217 | - const cjsModuleCache = ObjectGetValueSafe(Module, '_cache'); | ||
| 218 | - const cjsModules = ObjectKeys(cjsModuleCache); | ||
| 219 | - for (let i = 0; i < cjsModules.length; i++) { | ||
| 220 | - const key = cjsModules[i]; | ||
| 221 | - const module = ObjectGetValueSafe(cjsModuleCache, key); | ||
| 222 | - const value = WeakMapPrototypeGet(cjsSourceMapCache, module); | ||
| 223 | - if (value) { | ||
| 224 | - // This is okay because `obj` has a null prototype. | ||
| 225 | - obj[`file://${key}`] = { | ||
| 226 | - lineLengths: ObjectGetValueSafe(value, 'lineLengths'), | ||
| 227 | - data: ObjectGetValueSafe(value, 'data'), | ||
| 228 | - url: ObjectGetValueSafe(value, 'url') | ||
| 229 | - }; | ||
| 230 | - } | ||
| 215 | + for (const value of cjsSourceMapCache) { | ||
| 216 | + obj[ObjectGetValueSafe(value, 'filename')] = { | ||
| 217 | + lineLengths: ObjectGetValueSafe(value, 'lineLengths'), | ||
| 218 | + data: ObjectGetValueSafe(value, 'data'), | ||
| 219 | + url: ObjectGetValueSafe(value, 'url') | ||
| 220 | + }; | ||
| 231 | 221 | } | |
| 232 | 222 | } | |
| 233 | 223 | ||
| 234 | - // Attempt to lookup a source map, which is either attached to a file URI, or | ||
| 235 | - // keyed on an error instance. | ||
| 236 | - // TODO(bcoe): once WeakRefs are available in Node.js, refactor to drop | ||
| 237 | - // requirement of error parameter. | ||
| 238 | - function findSourceMap(uri, error) { | ||
| 239 | - if (!Module) Module = require('internal/modules/cjs/loader').Module; | ||
| 224 | + function findSourceMap(sourceURL) { | ||
| 225 | + if (!RegExpPrototypeTest(/^\w+:\/\//, sourceURL)) { | ||
| 226 | + sourceURL = pathToFileURL(sourceURL).href; | ||
| 227 | + } | ||
| 240 | 228 | if (!SourceMap) { | |
| 241 | 229 | SourceMap = require('internal/source_map/source_map').SourceMap; | |
| 242 | 230 | } | |
| 243 | - let sourceMap = cjsSourceMapCache.get(Module._cache[uri]); | ||
| 244 | - if (!uri.startsWith('file://')) uri = normalizeReferrerURL(uri); | ||
| 245 | - if (sourceMap === undefined) { | ||
| 246 | - sourceMap = esmSourceMapCache.get(uri); | ||
| 247 | - } | ||
| 231 | + let sourceMap = esmSourceMapCache.get(sourceURL); | ||
| 248 | 232 | if (sourceMap === undefined) { | |
| 249 | - const candidateSourceMap = cjsSourceMapCache.get(error); | ||
| 250 | - if (candidateSourceMap && uri === candidateSourceMap.filename) { | ||
| 251 | - sourceMap = candidateSourceMap; | ||
| 233 | + for (const value of cjsSourceMapCache) { | ||
| 234 | + const filename = ObjectGetValueSafe(value, 'filename'); | ||
| 235 | + if (sourceURL === filename) { | ||
| 236 | + sourceMap = { | ||
| 237 | + data: ObjectGetValueSafe(value, 'data') | ||
| 238 | + }; | ||
| 239 | + } | ||
| 252 | 240 | } | |
| 253 | 241 | } | |
| 254 | 242 | if (sourceMap && sourceMap.data) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,86 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const { | ||
| 4 | + makeSafe, | ||
| 5 | + Object, | ||
| 6 | + SafeSet, | ||
| 7 | + SafeWeakMap, | ||
| 8 | + SymbolIterator, | ||
| 9 | + } = primordials; | ||
| 10 | + | ||
| 11 | + // TODO(aduh95): Add FinalizationRegistry to primordials | ||
| 12 | + const SafeFinalizationRegistry = makeSafe( | ||
| 13 | + globalThis.FinalizationRegistry, | ||
| 14 | + class SafeFinalizationRegistry extends globalThis.FinalizationRegistry {} | ||
| 15 | + ); | ||
| 16 | + | ||
| 17 | + // TODO(aduh95): Add WeakRef to primordials | ||
| 18 | + const SafeWeakRef = makeSafe( | ||
| 19 | + globalThis.WeakRef, | ||
| 20 | + class SafeWeakRef extends globalThis.WeakRef {} | ||
| 21 | + ); | ||
| 22 | + | ||
| 23 | + // This class is modified from the example code in the WeakRefs specification: | ||
| 24 | + // https://github.com/tc39/proposal-weakrefs | ||
| 25 | + // Licensed under ECMA's MIT-style license, see: | ||
| 26 | + // https://github.com/tc39/ecma262/blob/master/LICENSE.md | ||
| 27 | + class IterableWeakMap { | ||
| 28 | + #weakMap = new SafeWeakMap(); | ||
| 29 | + #refSet = new SafeSet(); | ||
| 30 | + #finalizationGroup = new SafeFinalizationRegistry(cleanup); | ||
| 31 | + | ||
| 32 | + set(key, value) { | ||
| 33 | + const entry = this.#weakMap.get(key); | ||
| 34 | + if (entry) { | ||
| 35 | + // If there's already an entry for the object represented by "key", | ||
| 36 | + // the value can be updated without creating a new WeakRef: | ||
| 37 | + this.#weakMap.set(key, { value, ref: entry.ref }); | ||
| 38 | + } else { | ||
| 39 | + const ref = new SafeWeakRef(key); | ||
| 40 | + this.#weakMap.set(key, { value, ref }); | ||
| 41 | + this.#refSet.add(ref); | ||
| 42 | + this.#finalizationGroup.register(key, { | ||
| 43 | + set: this.#refSet, | ||
| 44 | + ref | ||
| 45 | + }, ref); | ||
| 46 | + } | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + get(key) { | ||
| 50 | + return this.#weakMap.get(key)?.value; | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + has(key) { | ||
| 54 | + return this.#weakMap.has(key); | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + delete(key) { | ||
| 58 | + const entry = this.#weakMap.get(key); | ||
| 59 | + if (!entry) { | ||
| 60 | + return false; | ||
| 61 | + } | ||
| 62 | + this.#weakMap.delete(key); | ||
| 63 | + this.#refSet.delete(entry.ref); | ||
| 64 | + this.#finalizationGroup.unregister(entry.ref); | ||
| 65 | + return true; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + *[SymbolIterator]() { | ||
| 69 | + for (const ref of this.#refSet) { | ||
| 70 | + const key = ref.deref(); | ||
| 71 | + if (!key) continue; | ||
| 72 | + const { value } = this.#weakMap.get(key); | ||
| 73 | + yield value; | ||
| 74 | + } | ||
| 75 | + } | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + function cleanup({ set, ref }) { | ||
| 79 | + set.delete(ref); | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + Object.freeze(IterableWeakMap.prototype); | ||
| 83 | + | ||
| 84 | + module.exports = { | ||
| 85 | + IterableWeakMap, | ||
| 86 | + }; | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -227,6 +227,7 @@ | |||
| 227 | 227 | 'lib/internal/util/debuglog.js', | |
| 228 | 228 | 'lib/internal/util/inspect.js', | |
| 229 | 229 | 'lib/internal/util/inspector.js', | |
| 230 | + 'lib/internal/util/iterable_weak_map.js', | ||
| 230 | 231 | 'lib/internal/util/types.js', | |
| 231 | 232 | 'lib/internal/http2/core.js', | |
| 232 | 233 | 'lib/internal/http2/compat.js', | |
| Back | FazBrowse Home | New Git URL |
0 commit comments