Skip to content

Commit bb88d0e

Browse files
committed
Do not mutate state shared with the old SourceMapGenerator after calling
SourceMapConsumer.fromSourceMap.
1 parent f28eb32 commit bb88d0e

2 files changed

Lines changed: 54 additions & 10 deletions

File tree

lib/source-map/source-map-consumer.js

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -337,25 +337,45 @@ define(function (require, exports, module) {
337337
function SourceMapConsumer_fromSourceMap(aSourceMap) {
338338
var smc = Object.create(BasicSourceMapConsumer.prototype);
339339

340-
smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
341-
smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
340+
var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
341+
var sources = smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
342342
smc.sourceRoot = aSourceMap._sourceRoot;
343343
smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
344344
smc.sourceRoot);
345345
smc.file = aSourceMap._file;
346346

347-
smc.__generatedMappings = aSourceMap._mappings.toArray().slice();
348-
smc.__originalMappings = aSourceMap._mappings.toArray().slice().sort();
347+
// Because we are modifying the entries (by converting string sources and
348+
// names to indices into the sources and names ArraySets), we have to make
349+
// a copy of the entry or else bad things happen. Shared mutable state
350+
// strikes again! See github issue #191.
349351

350-
smc.__generatedMappings.forEach(function (m) {
351-
if (m.source !== null) {
352-
m.source = smc._sources.indexOf(m.source);
352+
var generatedMappings = aSourceMap._mappings.toArray().slice();
353+
var destGeneratedMappings = smc.__generatedMappings = [];
354+
var destOriginalMappings = smc.__originalMappings = [];
353355

354-
if (m.name !== null) {
355-
m.name = smc._names.indexOf(m.name);
356+
for (var i = 0, length = generatedMappings.length; i < length; i++) {
357+
var srcMapping = generatedMappings[i];
358+
var destMapping = new Mapping;
359+
destMapping.generatedLine = srcMapping.generatedLine;
360+
destMapping.generatedColumn = srcMapping.generatedColumn;
361+
362+
if (srcMapping.source) {
363+
destMapping.source = sources.indexOf(srcMapping.source);
364+
destMapping.originalLine = srcMapping.originalLine;
365+
destMapping.originalColumn = srcMapping.originalColumn;
366+
367+
if (srcMapping.name) {
368+
destMapping.name = names.indexOf(srcMapping.name);
356369
}
370+
371+
destOriginalMappings.push(destMapping);
357372
}
358-
});
373+
374+
destGeneratedMappings.push(destMapping);
375+
}
376+
377+
quickSort(smc.__originalMappings, util.compareByOriginalPositions);
378+
359379
return smc;
360380
};
361381

test/source-map/test-source-map-consumer.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,4 +1057,28 @@ define(function (require, exports, module) {
10571057
assert.equal(pos.column, 4);
10581058
};
10591059

1060+
exports['test issue #191'] = function (assert, util) {
1061+
var generator = new SourceMapGenerator({ file: 'a.css' });
1062+
generator.addMapping({
1063+
source: 'b.css',
1064+
original: {
1065+
line: 1,
1066+
column: 0
1067+
},
1068+
generated: {
1069+
line: 1,
1070+
column: 0
1071+
}
1072+
});
1073+
1074+
// Create a SourceMapConsumer from the SourceMapGenerator, ...
1075+
var consumer = SourceMapConsumer.fromSourceMap(generator);
1076+
// ... and then try and use the SourceMapGenerator again. This should not
1077+
// throw.
1078+
generator.toJSON();
1079+
1080+
assert.ok(true, "Using a SourceMapGenerator again after creating a " +
1081+
"SourceMapConsumer from it should not throw");
1082+
};
1083+
10601084
});

0 commit comments

Comments
 (0)