[dart2js] Clear source location mappings after they've been processed. The data within this maps is apparently hanging around in memory longer than it needs to. We only read from each map in a short window and once it's been read we don't use that data again. By clearing the maps ater we use them we end up saving ~700MB (out of ~7.1GB) for phase 3b on a large program. References to all these maps are being retained by this higher level map: https://github.com/dart-lang/sdk/blob/main/pkg/compiler/lib/src/io/code_output.dart#L143 This change clears the internal maps while that larger map is being iterated over. Change-Id: I132dbf469391f257685b05fa69c302f1f1c44e31 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/325940 Reviewed-by: Mayank Patke <fishythefish@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/io/code_output.dart b/pkg/compiler/lib/src/io/code_output.dart index 7dc22c0..ca9243a 100644 --- a/pkg/compiler/lib/src/io/code_output.dart +++ b/pkg/compiler/lib/src/io/code_output.dart
@@ -39,8 +39,11 @@ void forEachSourceLocation( void f(int targetOffset, SourceLocation sourceLocation)); - /// Recorded inlining data per target-offset. - Map<int, List<FrameEntry>> get frameMarkers; + /// Applies [f] to every target offset and associated frame entry. This is + /// mostly used to track inlining data. + void forEachFrameMarker(void f(int targetOffset, FrameEntry frameEntry)); + + void close(); } class _SourceLocationsImpl implements SourceLocations { @@ -48,14 +51,15 @@ final String name; final AbstractCodeOutput codeOutput; Map<int, List<SourceLocation>> markers = {}; - @override Map<int, List<FrameEntry>> frameMarkers = {}; + bool _closed = false; _SourceLocationsImpl(this.name, this.codeOutput); @override void addSourceLocation(int targetOffset, SourceLocation sourceLocation) { assert(targetOffset <= codeOutput.length); + if (_closed) throw UnsupportedError('SourceLocations already closed.'); List<SourceLocation> sourceLocations = markers.putIfAbsent(targetOffset, () => []); sourceLocations.add(sourceLocation); @@ -65,6 +69,7 @@ void addPush(int targetOffset, SourceLocation? sourceLocation, String inlinedMethodName) { assert(targetOffset <= codeOutput.length); + if (_closed) throw UnsupportedError('SourceLocations already closed.'); List<FrameEntry> frames = frameMarkers[targetOffset] ??= []; frames.add(FrameEntry.push(sourceLocation, inlinedMethodName)); } @@ -72,6 +77,7 @@ @override void addPop(int targetOffset, bool isEmpty) { assert(targetOffset <= codeOutput.length); + if (_closed) throw UnsupportedError('SourceLocations already closed.'); List<FrameEntry> frames = frameMarkers[targetOffset] ??= []; frames.add(FrameEntry.pop(isEmpty)); } @@ -79,6 +85,7 @@ @override void forEachSourceLocation( void f(int targetOffset, SourceLocation sourceLocation)) { + if (_closed) throw UnsupportedError('SourceLocations already closed.'); markers.forEach((int targetOffset, List<SourceLocation> sourceLocations) { for (SourceLocation sourceLocation in sourceLocations) { f(targetOffset, sourceLocation); @@ -86,8 +93,27 @@ }); } + @override + void forEachFrameMarker(void f(int targetOffset, FrameEntry sourceLocation)) { + if (_closed) throw UnsupportedError('SourceLocations already closed.'); + frameMarkers.forEach((int targetOffset, List<FrameEntry> frameEntries) { + for (FrameEntry entry in frameEntries) { + f(targetOffset, entry); + } + }); + } + + @override + void close() { + if (_closed) throw UnsupportedError('SourceLocations already closed.'); + _closed = true; + frameMarkers.clear(); + markers.clear(); + } + void _merge(_SourceLocationsImpl other) { assert(name == other.name); + if (_closed) throw UnsupportedError('SourceLocations already closed.'); int length = codeOutput.length; if (other.markers.length > 0) { other.markers
diff --git a/pkg/compiler/lib/src/io/source_map_builder.dart b/pkg/compiler/lib/src/io/source_map_builder.dart index 4897306..e2dd8b4 100644 --- a/pkg/compiler/lib/src/io/source_map_builder.dart +++ b/pkg/compiler/lib/src/io/source_map_builder.dart
@@ -29,8 +29,8 @@ final Map<String, String> minifiedGlobalNames; final Map<String, String> minifiedInstanceNames; - /// Extension used to deobfuscate inlined stack frames. - final Map<int, List<FrameEntry>> frames; + /// Contains mapped source locations including inlined frame mappings. + final SourceLocations sourceLocations; SourceMapBuilder( this.version, @@ -39,7 +39,7 @@ this.locationProvider, this.minifiedGlobalNames, this.minifiedInstanceNames, - this.frames); + this.sourceLocations); void addMapping(int targetOffset, SourceLocation sourceLocation) { entries.add(SourceMapEntry(sourceLocation, targetOffset)); @@ -104,14 +104,12 @@ minifiedGlobalNames.values.forEach(nameMap.register); minifiedInstanceNames.values.forEach(nameMap.register); - for (List<FrameEntry> entries in frames.values) { - for (var frame in entries) { - registerLocation(frame.pushLocation); - if (frame.inlinedMethodName != null) { - nameMap.register(frame.inlinedMethodName!); - } + sourceLocations.forEachFrameMarker((_, frame) { + registerLocation(frame.pushLocation); + if (frame.inlinedMethodName != null) { + nameMap.register(frame.inlinedMethodName!); } - } + }); StringBuffer mappingsBuffer = StringBuffer(); writeEntries(lineColumnMap, uriMap, nameMap, mappingsBuffer); @@ -230,21 +228,19 @@ var columnEncoder = DeltaEncoder(); var nameEncoder = DeltaEncoder(); buffer.write('"'); - frames.forEach((int offset, List<FrameEntry> entries) { - for (var entry in entries) { - offsetEncoder.encode(buffer, offset); - if (entry.isPush) { - SourceLocation location = entry.pushLocation!; - uriEncoder.encode(buffer, uriMap[location.sourceUri!]!); - lineEncoder.encode(buffer, location.line - 1); - columnEncoder.encode(buffer, location.column - 1); - nameEncoder.encode(buffer, nameMap[entry.inlinedMethodName!]!); - } else { - // ; and , are not used by VLQ so we can distinguish them in the - // encoding, this is the same reason they are used in the mappings - // field. - buffer.write(entry.isEmptyPop ? ";" : ","); - } + sourceLocations.forEachFrameMarker((int offset, FrameEntry entry) { + offsetEncoder.encode(buffer, offset); + if (entry.isPush) { + SourceLocation location = entry.pushLocation!; + uriEncoder.encode(buffer, uriMap[location.sourceUri!]!); + lineEncoder.encode(buffer, location.line - 1); + columnEncoder.encode(buffer, location.column - 1); + nameEncoder.encode(buffer, nameMap[entry.inlinedMethodName!]!); + } else { + // ; and , are not used by VLQ so we can distinguish them in the + // encoding, this is the same reason they are used in the mappings + // field. + buffer.write(entry.isEmptyPop ? ";" : ","); } }); buffer.write('"'); @@ -289,9 +285,10 @@ locationProvider, minifiedGlobalNames, minifiedInstanceNames, - sourceLocations.frameMarkers); + sourceLocations); sourceLocations.forEachSourceLocation(sourceMapBuilder.addMapping); String sourceMap = sourceMapBuilder.build(); + sourceLocations.close(); String extension = 'js.map'; if (index > 0) { if (name == '') {