| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 5f4535a commit 7b41874
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,28 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | 2 | ||
| 3 | + const { | ||
| 4 | + JSON, | ||
| 5 | + Object: { | ||
| 6 | + create: ObjectCreate, | ||
| 7 | + keys: ObjectKeys, | ||
| 8 | + getOwnPropertyDescriptor: ObjectGetOwnPropertyDescriptor, | ||
| 9 | + }, | ||
| 10 | + ObjectPrototype: { | ||
| 11 | + hasOwnProperty: ObjectHasOwnProperty | ||
| 12 | + }, | ||
| 13 | + MapPrototype: { | ||
| 14 | + entries: MapEntries | ||
| 15 | + }, uncurryThis | ||
| 16 | + } = primordials; | ||
| 17 | + | ||
| 18 | + const MapIteratorNext = uncurryThis(MapEntries(new Map()).next); | ||
| 19 | + const WeakMapGet = uncurryThis(WeakMap.prototype.get); | ||
| 20 | + | ||
| 21 | + function ObjectGetValueSafe(obj, key) { | ||
| 22 | + const desc = ObjectGetOwnPropertyDescriptor(obj, key); | ||
| 23 | + return ObjectHasOwnProperty(desc, 'value') ? desc.value : undefined; | ||
| 24 | + } | ||
| 25 | + | ||
| 3 | 26 | // See https://sourcemaps.info/spec.html for SourceMap V3 specification. | |
| 4 | 27 | const { Buffer } = require('buffer'); | |
| 5 | 28 | const debug = require('internal/util/debuglog').debuglog('source_map'); | |
@@ -9,14 +32,14 @@ const { getOptionValue } = require('internal/options'); | |||
| 9 | 32 | const { | |
| 10 | 33 | normalizeReferrerURL, | |
| 11 | 34 | } = require('internal/modules/cjs/helpers'); | |
| 12 | - const { JSON, Object } = primordials; | ||
| 13 | 35 | // For cjs, since Module._cache is exposed to users, we use a WeakMap | |
| 14 | 36 | // keyed on module, facilitating garbage collection. | |
| 15 | 37 | const cjsSourceMapCache = new WeakMap(); | |
| 16 | 38 | // The esm cache is not exposed to users, so we can use a Map keyed | |
| 17 | 39 | // on filenames. | |
| 18 | 40 | const esmSourceMapCache = new Map(); | |
| 19 | 41 | const { fileURLToPath, URL } = require('url'); | |
| 42 | + let Module; | ||
| 20 | 43 | ||
| 21 | 44 | let experimentalSourceMaps; | |
| 22 | 45 | function maybeCacheSourceMap(filename, content, cjsModuleInstance) { | |
@@ -40,6 +63,7 @@ function maybeCacheSourceMap(filename, content, cjsModuleInstance) { | |||
| 40 | 63 | const data = dataFromUrl(basePath, match.groups.sourceMappingURL); | |
| 41 | 64 | const url = data ? null : match.groups.sourceMappingURL; | |
| 42 | 65 | if (cjsModuleInstance) { | |
| 66 | + if (!Module) Module = require('internal/modules/cjs/loader').Module; | ||
| 43 | 67 | cjsSourceMapCache.set(cjsModuleInstance, { | |
| 44 | 68 | filename, | |
| 45 | 69 | lineLengths: lineLengths(content), | |
@@ -148,17 +172,27 @@ function rekeySourceMap(cjsModuleInstance, newInstance) { | |||
| 148 | 172 | } | |
| 149 | 173 | } | |
| 150 | 174 | ||
| 175 | + // WARNING: The `sourceMapCacheToObject` and `appendCJSCache` run during | ||
| 176 | + // shutdown. In particular, they also run when Workers are terminated, making | ||
| 177 | + // it important that they do not call out to any user-provided code, including | ||
| 178 | + // built-in prototypes that might have been tampered with. | ||
| 179 | + | ||
| 151 | 180 | // Get serialized representation of source-map cache, this is used | |
| 152 | 181 | // to persist a cache of source-maps to disk when NODE_V8_COVERAGE is enabled. | |
| 153 | 182 | function sourceMapCacheToObject() { | |
| 154 | - const obj = Object.create(null); | ||
| 183 | + const obj = ObjectCreate(null); | ||
| 155 | 184 | ||
| 156 | - for (const [k, v] of esmSourceMapCache) { | ||
| 185 | + const it = MapEntries(esmSourceMapCache); | ||
| 186 | + let entry; | ||
| 187 | + while (!(entry = MapIteratorNext(it)).done) { | ||
| 188 | + const k = entry.value[0]; | ||
| 189 | + const v = entry.value[1]; | ||
| 157 | 190 | obj[k] = v; | |
| 158 | 191 | } | |
| 192 | + | ||
| 159 | 193 | appendCJSCache(obj); | |
| 160 | 194 | ||
| 161 | - if (Object.keys(obj).length === 0) { | ||
| 195 | + if (ObjectKeys(obj).length === 0) { | ||
| 162 | 196 | return undefined; | |
| 163 | 197 | } else { | |
| 164 | 198 | return obj; | |
@@ -171,23 +205,28 @@ function sourceMapCacheToObject() { | |||
| 171 | 205 | // TODO(bcoe): this means we don't currently serialize source-maps attached | |
| 172 | 206 | // to error instances, only module instances. | |
| 173 | 207 | function appendCJSCache(obj) { | |
| 174 | - const { Module } = require('internal/modules/cjs/loader'); | ||
| 175 | - Object.keys(Module._cache).forEach((key) => { | ||
| 176 | - const value = cjsSourceMapCache.get(Module._cache[key]); | ||
| 208 | + if (!Module) return; | ||
| 209 | + const cjsModuleCache = ObjectGetValueSafe(Module, '_cache'); | ||
| 210 | + const cjsModules = ObjectKeys(cjsModuleCache); | ||
| 211 | + for (let i = 0; i < cjsModules.length; i++) { | ||
| 212 | + const key = cjsModules[i]; | ||
| 213 | + const module = ObjectGetValueSafe(cjsModuleCache, key); | ||
| 214 | + const value = WeakMapGet(cjsSourceMapCache, module); | ||
| 177 | 215 | if (value) { | |
| 216 | + // This is okay because `obj` has a null prototype. | ||
| 178 | 217 | obj[`file://${key}`] = { | |
| 179 | - lineLengths: value.lineLengths, | ||
| 180 | - data: value.data, | ||
| 181 | - url: value.url | ||
| 218 | + lineLengths: ObjectGetValueSafe(value, 'lineLengths'), | ||
| 219 | + data: ObjectGetValueSafe(value, 'data'), | ||
| 220 | + url: ObjectGetValueSafe(value, 'url') | ||
| 182 | 221 | }; | |
| 183 | 222 | } | |
| 184 | - }); | ||
| 223 | + } | ||
| 185 | 224 | } | |
| 186 | 225 | ||
| 187 | 226 | // Attempt to lookup a source map, which is either attached to a file URI, or | |
| 188 | 227 | // keyed on an error instance. | |
| 189 | 228 | function findSourceMap(uri, error) { | |
| 190 | - const { Module } = require('internal/modules/cjs/loader'); | ||
| 229 | + if (!Module) Module = require('internal/modules/cjs/loader').Module; | ||
| 191 | 230 | let sourceMap = cjsSourceMapCache.get(Module._cache[uri]); | |
| 192 | 231 | if (!uri.startsWith('file://')) uri = normalizeReferrerURL(uri); | |
| 193 | 232 | if (sourceMap === undefined) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,40 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + const common = require('../common'); | ||
| 3 | + const tmpdir = require('../common/tmpdir'); | ||
| 4 | + const assert = require('assert'); | ||
| 5 | + | ||
| 6 | + // Attempts to test that the source map JS code run on process shutdown | ||
| 7 | + // does not call any user-defined JS code. | ||
| 8 | + | ||
| 9 | + const { Worker, workerData, parentPort } = require('worker_threads'); | ||
| 10 | + | ||
| 11 | + if (!workerData) { | ||
| 12 | + tmpdir.refresh(); | ||
| 13 | + process.env.NODE_V8_COVERAGE = tmpdir.path; | ||
| 14 | + | ||
| 15 | + // Count the number of some calls that should not be made. | ||
| 16 | + const callCount = new Int32Array(new SharedArrayBuffer(4)); | ||
| 17 | + const w = new Worker(__filename, { workerData: { callCount } }); | ||
| 18 | + w.on('message', common.mustCall(() => w.terminate())); | ||
| 19 | + w.on('exit', common.mustCall(() => { | ||
| 20 | + assert.strictEqual(callCount[0], 0); | ||
| 21 | + })); | ||
| 22 | + return; | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + const { callCount } = workerData; | ||
| 26 | + | ||
| 27 | + function increaseCallCount() { callCount[0]++; } | ||
| 28 | + | ||
| 29 | + // Increase the call count when a forbidden method is called. | ||
| 30 | + Object.getPrototypeOf((new Map()).entries()).next = increaseCallCount; | ||
| 31 | + Map.prototype.entries = increaseCallCount; | ||
| 32 | + Object.keys = increaseCallCount; | ||
| 33 | + Object.create = increaseCallCount; | ||
| 34 | + Object.hasOwnProperty = increaseCallCount; | ||
| 35 | + Object.defineProperty(Object.prototype, 'value', { | ||
| 36 | + get: increaseCallCount, | ||
| 37 | + set: increaseCallCount | ||
| 38 | + }); | ||
| 39 | + | ||
| 40 | + parentPort.postMessage('done'); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments