diff --git a/lib/internal/source_map/source_map.js b/lib/internal/source_map/source_map.js index dbfc05e8925d46..42e7bca3c4c5c0 100644 --- a/lib/internal/source_map/source_map.js +++ b/lib/internal/source_map/source_map.js @@ -71,6 +71,7 @@ const { ArrayPrototypePush, ArrayPrototypeSlice, ArrayPrototypeSort, + ObjectFreeze, ObjectPrototypeHasOwnProperty, StringPrototypeCharAt, Symbol, @@ -144,7 +145,7 @@ class SourceMap { this.#payload = cloneSourceMapV3(payload); this.#parseMappingPayload(); if (ArrayIsArray(lineLengths) && lineLengths.length) { - this.#lineLengths = lineLengths; + this.#lineLengths = ObjectFreeze(ArrayPrototypeSlice(lineLengths)); } } @@ -152,7 +153,7 @@ class SourceMap { * @returns {object} raw source map v3 payload. */ get payload() { - return cloneSourceMapV3(this.#payload); + return this.#payload; } get [kMappings]() { @@ -163,10 +164,7 @@ class SourceMap { * @returns {number[] | undefined} line lengths of generated source code */ get lineLengths() { - if (this.#lineLengths) { - return ArrayPrototypeSlice(this.#lineLengths); - } - return undefined; + return this.#lineLengths; } #parseMappingPayload = () => { @@ -366,10 +364,10 @@ function cloneSourceMapV3(payload) { for (const key in payload) { if (ObjectPrototypeHasOwnProperty(payload, key) && ArrayIsArray(payload[key])) { - payload[key] = ArrayPrototypeSlice(payload[key]); + payload[key] = ObjectFreeze(ArrayPrototypeSlice(payload[key])); } } - return payload; + return ObjectFreeze(payload); } /** diff --git a/test/parallel/test-source-map-api.js b/test/parallel/test-source-map-api.js index 8c965891536072..bc09d2ca2b8775 100644 --- a/test/parallel/test-source-map-api.js +++ b/test/parallel/test-source-map-api.js @@ -140,6 +140,14 @@ const { readFileSync } = require('fs'); assert.notStrictEqual(payload, sourceMap.payload); assert.strictEqual(payload.sources[0], sourceMap.payload.sources[0]); assert.notStrictEqual(payload.sources, sourceMap.payload.sources); + // The payload and its arrays should be frozen to avoid unnecessary cloning: + assert(Object.isFrozen(sourceMap.payload)); + assert(Object.isFrozen(sourceMap.payload.sources)); + // The same frozen object is returned on each call: + assert.strictEqual(sourceMap.payload, sourceMap.payload); + // lineLengths should be frozen and return the same reference each call: + assert(Object.isFrozen(sourceMap.lineLengths)); + assert.strictEqual(sourceMap.lineLengths, sourceMap.lineLengths); } // findEntry() and findOrigin() must return empty object instead of @@ -178,6 +186,11 @@ const { readFileSync } = require('fs'); assert.notStrictEqual(payload, sourceMap.payload); assert.strictEqual(payload.sources[0], sourceMap.payload.sources[0]); assert.notStrictEqual(payload.sources, sourceMap.payload.sources); + // The payload and its arrays should be frozen to avoid unnecessary cloning: + assert(Object.isFrozen(sourceMap.payload)); + assert(Object.isFrozen(sourceMap.payload.sources)); + // The same frozen object is returned on each call: + assert.strictEqual(sourceMap.payload, sourceMap.payload); } // Test various known decodings to ensure decodeVLQ works correctly.