Skip to content

Commit 3b8cc82

Browse files
committed
perf: use Object.freeze to avoid defensive cloning in SourceMap
1 parent 58a8e1d commit 3b8cc82

File tree

2 files changed

+19
-8
lines changed

2 files changed

+19
-8
lines changed

lib/internal/source_map/source_map.js

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ const {
7171
ArrayPrototypePush,
7272
ArrayPrototypeSlice,
7373
ArrayPrototypeSort,
74+
ObjectFreeze,
7475
ObjectPrototypeHasOwnProperty,
7576
StringPrototypeCharAt,
7677
Symbol,
@@ -144,15 +145,15 @@ class SourceMap {
144145
this.#payload = cloneSourceMapV3(payload);
145146
this.#parseMappingPayload();
146147
if (ArrayIsArray(lineLengths) && lineLengths.length) {
147-
this.#lineLengths = lineLengths;
148+
this.#lineLengths = ObjectFreeze(ArrayPrototypeSlice(lineLengths));
148149
}
149150
}
150151

151152
/**
152153
* @returns {object} raw source map v3 payload.
153154
*/
154155
get payload() {
155-
return cloneSourceMapV3(this.#payload);
156+
return this.#payload;
156157
}
157158

158159
get [kMappings]() {
@@ -163,10 +164,7 @@ class SourceMap {
163164
* @returns {number[] | undefined} line lengths of generated source code
164165
*/
165166
get lineLengths() {
166-
if (this.#lineLengths) {
167-
return ArrayPrototypeSlice(this.#lineLengths);
168-
}
169-
return undefined;
167+
return this.#lineLengths;
170168
}
171169

172170
#parseMappingPayload = () => {
@@ -366,10 +364,10 @@ function cloneSourceMapV3(payload) {
366364
for (const key in payload) {
367365
if (ObjectPrototypeHasOwnProperty(payload, key) &&
368366
ArrayIsArray(payload[key])) {
369-
payload[key] = ArrayPrototypeSlice(payload[key]);
367+
payload[key] = ObjectFreeze(ArrayPrototypeSlice(payload[key]));
370368
}
371369
}
372-
return payload;
370+
return ObjectFreeze(payload);
373371
}
374372

375373
/**

test/parallel/test-source-map-api.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,14 @@ const { readFileSync } = require('fs');
140140
assert.notStrictEqual(payload, sourceMap.payload);
141141
assert.strictEqual(payload.sources[0], sourceMap.payload.sources[0]);
142142
assert.notStrictEqual(payload.sources, sourceMap.payload.sources);
143+
// The payload and its arrays should be frozen to avoid unnecessary cloning:
144+
assert(Object.isFrozen(sourceMap.payload));
145+
assert(Object.isFrozen(sourceMap.payload.sources));
146+
// The same frozen object is returned on each call:
147+
assert.strictEqual(sourceMap.payload, sourceMap.payload);
148+
// lineLengths should be frozen and return the same reference each call:
149+
assert(Object.isFrozen(sourceMap.lineLengths));
150+
assert.strictEqual(sourceMap.lineLengths, sourceMap.lineLengths);
143151
}
144152

145153
// findEntry() and findOrigin() must return empty object instead of
@@ -178,6 +186,11 @@ const { readFileSync } = require('fs');
178186
assert.notStrictEqual(payload, sourceMap.payload);
179187
assert.strictEqual(payload.sources[0], sourceMap.payload.sources[0]);
180188
assert.notStrictEqual(payload.sources, sourceMap.payload.sources);
189+
// The payload and its arrays should be frozen to avoid unnecessary cloning:
190+
assert(Object.isFrozen(sourceMap.payload));
191+
assert(Object.isFrozen(sourceMap.payload.sources));
192+
// The same frozen object is returned on each call:
193+
assert.strictEqual(sourceMap.payload, sourceMap.payload);
181194
}
182195

183196
// Test various known decodings to ensure decodeVLQ works correctly.

0 commit comments

Comments
 (0)