Skip to content

Commit 43e3f35

Browse files
committed
Reduce allocations/gc by avoiding the creation of some CommentRange objects.
1 parent fcd4ef4 commit 43e3f35

6 files changed

Lines changed: 210 additions & 115 deletions

File tree

src/compiler/comments.ts

Lines changed: 98 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ namespace ts {
2323
let currentText: string;
2424
let currentLineMap: number[];
2525
let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[];
26-
27-
// Tracks comment ranges that have already been consumed.
28-
let consumedCommentRanges: Map<boolean>;
26+
let hasWrittenComment = false;
27+
let hasLastComment: boolean;
28+
let lastCommentEnd: number;
2929

3030
return {
3131
reset,
@@ -122,7 +122,7 @@ namespace ts {
122122
const skipTrailingComments = end < 0 || (emitFlags & NodeEmitFlags.NoTrailingComments) !== 0 || compilerOptions.removeComments;
123123

124124
if (!skipLeadingComments) {
125-
emitDetachedCommentsAndUpdateCommentsInfo(detachedRange, compilerOptions.removeComments);
125+
emitDetachedCommentsAndUpdateCommentsInfo(detachedRange);
126126
}
127127

128128
if (extendedDiagnostics) {
@@ -144,11 +144,12 @@ namespace ts {
144144
}
145145

146146
function emitLeadingComments(pos: number, isEmittedNode: boolean) {
147-
let leadingComments: CommentRange[];
147+
hasWrittenComment = false;
148+
148149
if (isEmittedNode) {
149-
leadingComments = getLeadingCommentsToEmit(pos);
150+
forEachLeadingCommentToEmit(pos, emitLeadingComment);
150151
}
151-
else {
152+
else if (pos === 0) {
152153
// If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node,
153154
// unless it is a triple slash comment at the top of the file.
154155
// For Example:
@@ -157,22 +158,52 @@ namespace ts {
157158
// /// <reference-path ...>
158159
// interface F {}
159160
// The first /// will NOT be removed while the second one will be removed even though both node will not be emitted
160-
if (pos === 0) {
161-
leadingComments = filter(getLeadingCommentsToEmit(pos), isTripleSlashComment);
162-
}
161+
forEachLeadingCommentToEmit(pos, emitTripleSlashLeadingComment);
163162
}
163+
}
164164

165-
emitNewLineBeforeLeadingCommentsOfPosition(currentLineMap, writer, pos, leadingComments);
165+
function emitTripleSlashLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
166+
if (isTripleSlashComment(commentPos, commentEnd)) {
167+
emitLeadingComment(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos);
168+
}
169+
}
170+
171+
function emitLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
172+
if (!hasWrittenComment) {
173+
emitNewLineBeforeLeadingCommentOfPosition(currentLineMap, writer, rangePos, commentPos);
174+
hasWrittenComment = true;
175+
}
166176

167177
// Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
168-
emitComments(currentText, currentLineMap, writer, leadingComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment);
178+
emitPos(commentPos);
179+
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
180+
emitPos(commentEnd);
181+
182+
if (hasTrailingNewLine) {
183+
writer.writeLine();
184+
}
185+
else {
186+
writer.write(" ");
187+
}
169188
}
170189

171190
function emitTrailingComments(pos: number) {
172-
const trailingComments = getTrailingCommentsToEmit(pos);
191+
forEachTrailingCommentToEmit(pos, emitTrailingComment);
192+
}
193+
194+
function emitTrailingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) {
195+
// trailing comments are emitted at space/*trailing comment1 */space/*trailing comment2*/
196+
if (!writer.isAtStartOfLine()) {
197+
writer.write(" ");
198+
}
199+
200+
emitPos(commentPos);
201+
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
202+
emitPos(commentEnd);
173203

174-
// trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/
175-
emitComments(currentText, currentLineMap, writer, trailingComments, /*leadingSeparator*/ true, /*trailingSeparator*/ false, newLine, writeComment);
204+
if (hasTrailingNewLine) {
205+
writer.writeLine();
206+
}
176207
}
177208

178209
function emitTrailingCommentsOfPosition(pos: number) {
@@ -185,88 +216,80 @@ namespace ts {
185216
commentStart = performance.mark();
186217
}
187218

188-
const trailingComments = getTrailingCommentsToEmit(pos);
189-
190-
// trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space
191-
emitComments(currentText, currentLineMap, writer, trailingComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment);
219+
forEachTrailingCommentToEmit(pos, emitTrailingCommentOfPosition);
192220

193221
if (extendedDiagnostics) {
194222
performance.measure("commentTime", commentStart);
195223
}
196224
}
197225

198-
function getLeadingCommentsToEmit(pos: number) {
226+
function emitTrailingCommentOfPosition(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) {
227+
// trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space
228+
229+
emitPos(commentPos);
230+
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
231+
emitPos(commentEnd);
232+
233+
if (hasTrailingNewLine) {
234+
writer.writeLine();
235+
}
236+
else {
237+
writer.write(" ");
238+
}
239+
}
240+
241+
function forEachLeadingCommentToEmit(pos: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) {
199242
// Emit the leading comments only if the container's pos doesn't match because the container should take care of emitting these comments
200243
if (containerPos === -1 || pos !== containerPos) {
201-
return hasDetachedComments(pos)
202-
? getLeadingCommentsWithoutDetachedComments()
203-
: getLeadingCommentRanges(currentText, pos);
244+
if (hasDetachedComments(pos)) {
245+
forEachLeadingCommentWithoutDetachedComments(cb);
246+
}
247+
else {
248+
forEachLeadingCommentRange(currentText, pos, cb, /*state*/ pos);
249+
}
204250
}
205251
}
206252

207-
function getTrailingCommentsToEmit(end: number) {
253+
function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) {
208254
// Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments
209255
if (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd)) {
210-
return getTrailingCommentRanges(currentText, end);
256+
forEachTrailingCommentRange(currentText, end, cb);
211257
}
212258
}
213259

214-
/**
215-
* Determine if the given comment is a triple-slash
216-
*
217-
* @return true if the comment is a triple-slash comment else false
218-
**/
219-
function isTripleSlashComment(comment: CommentRange) {
220-
// Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text
221-
// so that we don't end up computing comment string and doing match for all // comments
222-
if (currentText.charCodeAt(comment.pos + 1) === CharacterCodes.slash &&
223-
comment.pos + 2 < comment.end &&
224-
currentText.charCodeAt(comment.pos + 2) === CharacterCodes.slash) {
225-
const textSubStr = currentText.substring(comment.pos, comment.end);
226-
return textSubStr.match(fullTripleSlashReferencePathRegEx) ||
227-
textSubStr.match(fullTripleSlashAMDReferencePathRegEx) ?
228-
true : false;
229-
}
230-
return false;
231-
}
232-
233260
function reset() {
234261
currentSourceFile = undefined;
235262
currentText = undefined;
236263
currentLineMap = undefined;
237264
detachedCommentsInfo = undefined;
238-
consumedCommentRanges = undefined;
239265
}
240266

241267
function setSourceFile(sourceFile: SourceFile) {
242268
currentSourceFile = sourceFile;
243269
currentText = currentSourceFile.text;
244270
currentLineMap = getLineStarts(currentSourceFile);
245271
detachedCommentsInfo = undefined;
246-
consumedCommentRanges = {};
247272
}
248273

249274
function hasDetachedComments(pos: number) {
250275
return detachedCommentsInfo !== undefined && lastOrUndefined(detachedCommentsInfo).nodePos === pos;
251276
}
252277

253-
function getLeadingCommentsWithoutDetachedComments() {
278+
function forEachLeadingCommentWithoutDetachedComments(cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) {
254279
// get the leading comments from detachedPos
255280
const pos = lastOrUndefined(detachedCommentsInfo).detachedCommentEndPos;
256-
const leadingComments = getLeadingCommentRanges(currentText, pos);
257281
if (detachedCommentsInfo.length - 1) {
258282
detachedCommentsInfo.pop();
259283
}
260284
else {
261285
detachedCommentsInfo = undefined;
262286
}
263287

264-
return leadingComments;
288+
forEachLeadingCommentRange(currentText, pos, cb, /*state*/ pos);
265289
}
266290

267-
function emitDetachedCommentsAndUpdateCommentsInfo(node: TextRange, removeComments: boolean) {
268-
const currentDetachedCommentInfo = emitDetachedComments(currentText, currentLineMap, writer, writeComment, node, newLine, removeComments);
269-
291+
function emitDetachedCommentsAndUpdateCommentsInfo(range: TextRange) {
292+
const currentDetachedCommentInfo = emitDetachedComments(currentText, currentLineMap, writer, writeComment, range, newLine, compilerOptions.removeComments);
270293
if (currentDetachedCommentInfo) {
271294
if (detachedCommentsInfo) {
272295
detachedCommentsInfo.push(currentDetachedCommentInfo);
@@ -277,10 +300,29 @@ namespace ts {
277300
}
278301
}
279302

280-
function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, comment: CommentRange, newLine: string) {
281-
emitPos(comment.pos);
282-
writeCommentRange(text, lineMap, writer, comment, newLine);
283-
emitPos(comment.end);
303+
function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) {
304+
emitPos(commentPos);
305+
writeCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine);
306+
emitPos(commentEnd);
307+
}
308+
309+
/**
310+
* Determine if the given comment is a triple-slash
311+
*
312+
* @return true if the comment is a triple-slash comment else false
313+
**/
314+
function isTripleSlashComment(commentPos: number, commentEnd: number) {
315+
// Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text
316+
// so that we don't end up computing comment string and doing match for all // comments
317+
if (currentText.charCodeAt(commentPos + 1) === CharacterCodes.slash &&
318+
commentPos + 2 < commentEnd &&
319+
currentText.charCodeAt(commentPos + 2) === CharacterCodes.slash) {
320+
const textSubStr = currentText.substring(commentPos, commentEnd);
321+
return textSubStr.match(fullTripleSlashReferencePathRegEx) ||
322+
textSubStr.match(fullTripleSlashAMDReferencePathRegEx) ?
323+
true : false;
324+
}
325+
return false;
284326
}
285327
}
286328
}

src/compiler/core.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,8 +1139,8 @@ namespace ts {
11391139
/** Performance measurements for the compiler. */
11401140
/*@internal*/
11411141
export namespace performance {
1142-
let counters: Map<number> = {};
1143-
let measures: Map<number> = {};
1142+
let counters: Map<number>;
1143+
let measures: Map<number>;
11441144
let enabled = false;
11451145

11461146
/**
@@ -1191,22 +1191,32 @@ namespace ts {
11911191
return enabled && getProperty(measures, measureName) || 0;
11921192
}
11931193

1194-
/**
1195-
* Resets all marks and measurements in the performance service.
1196-
*/
1197-
export function reset() {
1198-
counters = {};
1199-
measures = {};
1200-
}
1201-
12021194
/** Enables performance measurements for the compiler. */
12031195
export function enable() {
1204-
enabled = true;
1196+
if (!enabled) {
1197+
enabled = true;
1198+
counters = { };
1199+
measures = {
1200+
programTime: 0,
1201+
parseTime: 0,
1202+
bindTime: 0,
1203+
emitTime: 0,
1204+
ioReadTime: 0,
1205+
ioWriteTime: 0,
1206+
printTime: 0,
1207+
commentTime: 0,
1208+
sourceMapTime: 0
1209+
};
1210+
}
12051211
}
12061212

12071213
/** Disables performance measurements for the compiler. */
12081214
export function disable() {
1209-
enabled = false;
1215+
if (enabled) {
1216+
enabled = false;
1217+
counters = undefined;
1218+
measures = undefined;
1219+
}
12101220
}
12111221
}
12121222
}

src/compiler/emitter.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8228,10 +8228,10 @@ const _super = (function (geti, seti) {
82288228
}
82298229
}
82308230

8231-
function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, comment: CommentRange, newLine: string) {
8232-
emitPos(comment.pos);
8233-
writeCommentRange(text, lineMap, writer, comment, newLine);
8234-
emitPos(comment.end);
8231+
function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) {
8232+
emitPos(commentPos);
8233+
writeCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine);
8234+
emitPos(commentEnd);
82358235
}
82368236

82378237
function emitShebang() {

0 commit comments

Comments
 (0)