Skip to content

Commit

Permalink
Compact the tsbuild info by encoding info differently for some of the…
Browse files Browse the repository at this point in the history
… situations (#58641)
  • Loading branch information
sheetalkamat committed May 24, 2024
1 parent cffc425 commit 87918f5
Show file tree
Hide file tree
Showing 608 changed files with 3,824 additions and 12,763 deletions.
127 changes: 84 additions & 43 deletions src/compiler/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
isNumber,
isString,
map,
mapDefinedIterator,
maybeBind,
noop,
notImplemented,
Expand Down Expand Up @@ -102,7 +103,7 @@ export interface ReusableDiagnostic extends ReusableDiagnosticRelatedInformation
export interface ReusableDiagnosticRelatedInformation {
category: DiagnosticCategory;
code: number;
file: string | undefined;
file: string | undefined | false;
start: number | undefined;
length: number | undefined;
messageText: string | ReusableDiagnosticMessageChain;
Expand Down Expand Up @@ -384,7 +385,7 @@ function createBuilderProgramState(newProgram: Program, oldState: Readonly<Reusa
(state.emitDiagnosticsPerFile ??= new Map()).set(
sourceFilePath,
oldState!.hasReusableDiagnostic ?
convertToDiagnostics(emitDiagnostics as readonly ReusableDiagnostic[], newProgram) :
convertToDiagnostics(emitDiagnostics as readonly ReusableDiagnostic[], sourceFilePath, newProgram) :
repopulateDiagnostics(emitDiagnostics as readonly Diagnostic[], newProgram),
);
}
Expand All @@ -399,7 +400,7 @@ function createBuilderProgramState(newProgram: Program, oldState: Readonly<Reusa
state.semanticDiagnosticsPerFile!.set(
sourceFilePath,
oldState!.hasReusableDiagnostic ?
convertToDiagnostics(diagnostics as readonly ReusableDiagnostic[], newProgram) :
convertToDiagnostics(diagnostics as readonly ReusableDiagnostic[], sourceFilePath, newProgram) :
repopulateDiagnostics(diagnostics as readonly Diagnostic[], newProgram),
);
(state.semanticDiagnosticsFromOldState ??= new Set()).add(sourceFilePath);
Expand Down Expand Up @@ -516,19 +517,19 @@ function convertOrRepopulateDiagnosticMessageChainArray<T extends DiagnosticMess
return sameMap(array, chain => convertOrRepopulateDiagnosticMessageChain(chain, sourceFile, newProgram, repopulateInfo));
}

function convertToDiagnostics(diagnostics: readonly ReusableDiagnostic[], newProgram: Program): readonly Diagnostic[] {
function convertToDiagnostics(diagnostics: readonly ReusableDiagnostic[], diagnosticFilePath: Path, newProgram: Program): readonly Diagnostic[] {
if (!diagnostics.length) return emptyArray;
let buildInfoDirectory: string | undefined;
return diagnostics.map(diagnostic => {
const result: Diagnostic = convertToDiagnosticRelatedInformation(diagnostic, newProgram, toPathInBuildInfoDirectory);
const result: Diagnostic = convertToDiagnosticRelatedInformation(diagnostic, diagnosticFilePath, newProgram, toPathInBuildInfoDirectory);
result.reportsUnnecessary = diagnostic.reportsUnnecessary;
result.reportsDeprecated = diagnostic.reportDeprecated;
result.source = diagnostic.source;
result.skippedOn = diagnostic.skippedOn;
const { relatedInformation } = diagnostic;
result.relatedInformation = relatedInformation ?
relatedInformation.length ?
relatedInformation.map(r => convertToDiagnosticRelatedInformation(r, newProgram, toPathInBuildInfoDirectory)) :
relatedInformation.map(r => convertToDiagnosticRelatedInformation(r, diagnosticFilePath, newProgram, toPathInBuildInfoDirectory)) :
[] :
undefined;
return result;
Expand All @@ -540,9 +541,11 @@ function convertToDiagnostics(diagnostics: readonly ReusableDiagnostic[], newPro
}
}

function convertToDiagnosticRelatedInformation(diagnostic: ReusableDiagnosticRelatedInformation, newProgram: Program, toPath: (path: string) => Path): DiagnosticRelatedInformation {
function convertToDiagnosticRelatedInformation(diagnostic: ReusableDiagnosticRelatedInformation, diagnosticFilePath: Path, newProgram: Program, toPath: (path: string) => Path): DiagnosticRelatedInformation {
const { file } = diagnostic;
const sourceFile = file ? newProgram.getSourceFileByPath(toPath(file)) : undefined;
const sourceFile = file !== false ?
newProgram.getSourceFileByPath(file ? toPath(file) : diagnosticFilePath) :
undefined;
return {
...diagnostic,
file: sourceFile,
Expand Down Expand Up @@ -981,7 +984,14 @@ export type ProgramBuildInfoFileId = number & { __programBuildInfoFileIdBrand: a
/** @internal */
export type ProgramBuildInfoFileIdListId = number & { __programBuildInfoFileIdListIdBrand: any; };
/** @internal */
export type ProgramBuildInfoDiagnostic = ProgramBuildInfoFileId | [fileId: ProgramBuildInfoFileId, diagnostics: readonly ReusableDiagnostic[]];
export type ProgramBuildInfoDiagnosticOfFile = [fileId: ProgramBuildInfoFileId, diagnostics: readonly ReusableDiagnostic[]];
/** @internal */
export type ProgramBuildInfoDiagnostic =
| ProgramBuildInfoFileId // File is not in changedSet and still doesnt have cached diagnostics
| ProgramBuildInfoDiagnosticOfFile; // Diagnostics for file
/** @internal */
export type ProgramBuildInfoEmitDiagnostic = ProgramBuildInfoDiagnosticOfFile; // Diagnostics for the file

/**
* fileId if pending emit is same as what compilerOptions suggest
* [fileId] if pending emit is only dts file emit
Expand Down Expand Up @@ -1034,7 +1044,7 @@ export interface ProgramMultiFileEmitBuildInfo {
fileIdsList: readonly (readonly ProgramBuildInfoFileId[])[] | undefined;
referencedMap: ProgramBuildInfoReferencedMap | undefined;
semanticDiagnosticsPerFile: ProgramBuildInfoDiagnostic[] | undefined;
emitDiagnosticsPerFile: ProgramBuildInfoDiagnostic[] | undefined;
emitDiagnosticsPerFile: ProgramBuildInfoEmitDiagnostic[] | undefined;
affectedFilesPendingEmit: ProgramBuilderInfoFilePendingEmit[] | undefined;
changeFileSet: readonly ProgramBuildInfoFileId[] | undefined;
emitSignatures: readonly ProgramBuildInfoEmitSignature[] | undefined;
Expand Down Expand Up @@ -1156,14 +1166,14 @@ function getBuildInfo(state: BuilderProgramState): BuildInfo {
});

let referencedMap: ProgramBuildInfoReferencedMap | undefined;
if (state.referencedMap) {
if (state.referencedMap?.size()) {
referencedMap = arrayFrom(state.referencedMap.keys()).sort(compareStringsCaseSensitive).map(key => [
toFileId(key),
toFileIdListId(state.referencedMap!.getValues(key)!),
]);
}

const semanticDiagnosticsPerFile = convertToProgramBuildInfoDiagnostics(state.semanticDiagnosticsPerFile);
const semanticDiagnosticsPerFile = convertToProgramBuildInfoDiagnostics();
let affectedFilesPendingEmit: ProgramBuilderInfoFilePendingEmit[] | undefined;
if (state.affectedFilesPendingEmit?.size) {
const fullEmitForOptions = getBuilderFileEmit(state.compilerOptions);
Expand Down Expand Up @@ -1191,7 +1201,7 @@ function getBuildInfo(state: BuilderProgramState): BuildInfo {
changeFileSet = append(changeFileSet, toFileId(path));
}
}
const emitDiagnosticsPerFile = convertToProgramBuildInfoDiagnostics(state.emitDiagnosticsPerFile);
const emitDiagnosticsPerFile = convertToProgramBuildInfoEmitDiagnostics();
const program: ProgramMultiFileEmitBuildInfo = {
fileNames,
fileInfos,
Expand Down Expand Up @@ -1301,48 +1311,63 @@ function getBuildInfo(state: BuilderProgramState): BuildInfo {
return value;
}

function convertToProgramBuildInfoDiagnostics(diagnostics: Map<Path, readonly Diagnostic[]> | undefined) {
function convertToProgramBuildInfoDiagnostics() {
let result: ProgramBuildInfoDiagnostic[] | undefined;
if (diagnostics) {
for (const key of arrayFrom(diagnostics.keys()).sort(compareStringsCaseSensitive)) {
const value = diagnostics.get(key)!;
result = append(
result,
value.length ?
[
toFileId(key),
convertToReusableDiagnostics(value),
] :
toFileId(key),
);
state.fileInfos.forEach((_value, key) => {
const value = state.semanticDiagnosticsPerFile?.get(key);
if (!value) {
if (!state.changedFilesSet.has(key)) result = append(result, toFileId(key));
}
else if (value.length) {
result = append(result, [
toFileId(key),
convertToReusableDiagnostics(value, key),
]);
}
});
return result;
}

function convertToProgramBuildInfoEmitDiagnostics() {
let result: ProgramBuildInfoEmitDiagnostic[] | undefined;
if (!state.emitDiagnosticsPerFile?.size) return result;
for (const key of arrayFrom(state.emitDiagnosticsPerFile.keys()).sort(compareStringsCaseSensitive)) {
const value = state.emitDiagnosticsPerFile.get(key)!;
result = append(result, [
toFileId(key),
convertToReusableDiagnostics(value, key),
]);
}
return result;
}

function convertToReusableDiagnostics(diagnostics: readonly Diagnostic[]): readonly ReusableDiagnostic[] {
function convertToReusableDiagnostics(diagnostics: readonly Diagnostic[], diagnosticFilePath: Path): readonly ReusableDiagnostic[] {
Debug.assert(!!diagnostics.length);
return diagnostics.map(diagnostic => {
const result: ReusableDiagnostic = convertToReusableDiagnosticRelatedInformation(diagnostic);
const result: ReusableDiagnostic = convertToReusableDiagnosticRelatedInformation(diagnostic, diagnosticFilePath);
result.reportsUnnecessary = diagnostic.reportsUnnecessary;
result.reportDeprecated = diagnostic.reportsDeprecated;
result.source = diagnostic.source;
result.skippedOn = diagnostic.skippedOn;
const { relatedInformation } = diagnostic;
result.relatedInformation = relatedInformation ?
relatedInformation.length ?
relatedInformation.map(r => convertToReusableDiagnosticRelatedInformation(r)) :
relatedInformation.map(r => convertToReusableDiagnosticRelatedInformation(r, diagnosticFilePath)) :
[] :
undefined;
return result;
});
}

function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRelatedInformation): ReusableDiagnosticRelatedInformation {
function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRelatedInformation, diagnosticFilePath: Path): ReusableDiagnosticRelatedInformation {
const { file } = diagnostic;
return {
...diagnostic,
file: file ? relativeToBuildInfo(file.resolvedPath) : undefined,
file: file ?
file.resolvedPath === diagnosticFilePath ?
undefined :
relativeToBuildInfo(file.resolvedPath) :
false,
messageText: isString(diagnostic.messageText) ? diagnostic.messageText : convertToReusableDiagnosticMessageChain(diagnostic.messageText),
};
}
Expand Down Expand Up @@ -1881,16 +1906,17 @@ export function createBuilderProgramUsingProgramBuildInfo(buildInfo: BuildInfo,
);
}
});
const changedFilesSet = new Set(map(program.changeFileSet, toFilePath));
const fullEmitForOptions = program.affectedFilesPendingEmit ? getBuilderFileEmit(program.options || {}) : undefined;
state = {
fileInfos,
compilerOptions: program.options ? convertToOptionsWithAbsolutePaths(program.options, toAbsolutePath) : {},
referencedMap: toManyToManyPathMap(program.referencedMap),
semanticDiagnosticsPerFile: toPerFileDiagnostics(program.semanticDiagnosticsPerFile),
emitDiagnosticsPerFile: toPerFileDiagnostics(program.emitDiagnosticsPerFile),
referencedMap: toManyToManyPathMap(program.referencedMap, program.options ?? {}),
semanticDiagnosticsPerFile: toPerFileSemanticDiagnostics(program.semanticDiagnosticsPerFile, fileInfos, changedFilesSet),
emitDiagnosticsPerFile: toPerFileEmitDiagnostics(program.emitDiagnosticsPerFile),
hasReusableDiagnostic: true,
affectedFilesPendingEmit: program.affectedFilesPendingEmit && arrayToMap(program.affectedFilesPendingEmit, value => toFilePath(isNumber(value) ? value : value[0]), value => toBuilderFileEmit(value, fullEmitForOptions!)),
changedFilesSet: new Set(map(program.changeFileSet, toFilePath)),
changedFilesSet,
latestChangedDtsFile,
emitSignatures: emitSignatures?.size ? emitSignatures : undefined,
};
Expand Down Expand Up @@ -1938,18 +1964,33 @@ export function createBuilderProgramUsingProgramBuildInfo(buildInfo: BuildInfo,
return filePathsSetList![fileIdsListId - 1];
}

function toManyToManyPathMap(referenceMap: ProgramBuildInfoReferencedMap | undefined): BuilderState.ManyToManyPathMap | undefined {
if (!referenceMap) {
return undefined;
}

const map = BuilderState.createManyToManyPathMap();
function toManyToManyPathMap(referenceMap: ProgramBuildInfoReferencedMap | undefined, options: CompilerOptions): BuilderState.ManyToManyPathMap | undefined {
const map = BuilderState.createReferencedMap(options);
if (!map || !referenceMap) return map;
referenceMap.forEach(([fileId, fileIdListId]) => map.set(toFilePath(fileId), toFilePathsSet(fileIdListId)));
return map;
}

function toPerFileDiagnostics(diagnostics: readonly ProgramBuildInfoDiagnostic[] | undefined): Map<Path, readonly ReusableDiagnostic[]> | undefined {
return diagnostics && arrayToMap(diagnostics, value => toFilePath(isNumber(value) ? value : value[0]), value => isNumber(value) ? emptyArray : value[1]);
function toPerFileSemanticDiagnostics(
diagnostics: readonly ProgramBuildInfoDiagnostic[] | undefined,
fileInfos: Map<Path, BuilderState.FileInfo>,
changedFilesSet: Set<Path>,
): Map<Path, readonly ReusableDiagnostic[]> | undefined {
const semanticDiagnostics = new Map<Path, readonly ReusableDiagnostic[]>(
mapDefinedIterator(
fileInfos.keys(),
key => !changedFilesSet.has(key) ? [key, emptyArray] : undefined,
),
);
diagnostics?.forEach(value => {
if (isNumber(value)) semanticDiagnostics.delete(toFilePath(value));
else semanticDiagnostics.set(toFilePath(value[0]), value[1]);
});
return semanticDiagnostics.size ? semanticDiagnostics : undefined;
}

function toPerFileEmitDiagnostics(diagnostics: readonly ProgramBuildInfoEmitDiagnostic[] | undefined): Map<Path, readonly ReusableDiagnostic[]> | undefined {
return diagnostics && arrayToMap(diagnostics, value => toFilePath(value[0]), value => value[1]);
}
}

Expand Down
15 changes: 11 additions & 4 deletions src/compiler/builderState.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
arrayFrom,
CancellationToken,
CompilerOptions,
computeSignatureWithDiagnostics,
CustomTransformers,
Debug,
Expand Down Expand Up @@ -110,6 +111,7 @@ export namespace BuilderState {
getKeys(v: Path): ReadonlySet<Path> | undefined;
getValues(k: Path): ReadonlySet<Path> | undefined;
keys(): IterableIterator<Path>;
size(): number;
}

export interface ManyToManyPathMap extends ReadonlyManyToManyPathMap {
Expand All @@ -123,6 +125,7 @@ export namespace BuilderState {
getKeys: v => reverse.get(v),
getValues: k => forward.get(k),
keys: () => forward.keys(),
size: () => forward.size,

deleteKey: k => {
(deleted ||= new Set<Path>()).add(k);
Expand Down Expand Up @@ -292,15 +295,19 @@ export namespace BuilderState {
return oldState && !oldState.referencedMap === !newReferencedMap;
}

export function createReferencedMap(options: CompilerOptions) {
return options.module !== ModuleKind.None && !options.outFile ?
createManyToManyPathMap() :
undefined;
}

/**
* Creates the state of file references and signature for the new program from oldState if it is safe
*/
export function create(newProgram: Program, oldState: Readonly<BuilderState> | undefined, disableUseFileVersionAsSignature: boolean): BuilderState {
const fileInfos = new Map<Path, FileInfo>();
const options = newProgram.getCompilerOptions();
const isOutFile = options.outFile;
const referencedMap = options.module !== ModuleKind.None && !isOutFile ?
createManyToManyPathMap() : undefined;
const referencedMap = createReferencedMap(options);
const useOldState = canReuseOldState(referencedMap, oldState);

// Ensure source files have parent pointers set
Expand All @@ -323,7 +330,7 @@ export namespace BuilderState {
version,
signature,
// No need to calculate affectsGlobalScope with --out since its not used at all
affectsGlobalScope: !isOutFile ? isFileAffectingGlobalScope(sourceFile) || undefined : undefined,
affectsGlobalScope: !options.outFile ? isFileAffectingGlobalScope(sourceFile) || undefined : undefined,
impliedFormat: sourceFile.impliedNodeFormat,
});
}
Expand Down
4 changes: 1 addition & 3 deletions src/compiler/tsbuildPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ import {
getWatchErrorSummaryDiagnosticMessage,
hasProperty,
identity,
isArray,
isIgnoredFileFromWildCardWatching,
isIncrementalCompilation,
isPackageJsonInfo,
Expand Down Expand Up @@ -105,7 +104,6 @@ import {
SemanticDiagnosticsBuilderProgram,
setGetSourceFileAsHashVersioned,
SharedExtendedConfigFileWatcher,
some,
SourceFile,
Status,
sys,
Expand Down Expand Up @@ -1698,7 +1696,7 @@ function getUpToDateStatusWorker<T extends BuilderProgram>(state: SolutionBuilde
(!project.options.noEmit ?
(buildInfo.program as ProgramMultiFileEmitBuildInfo).affectedFilesPendingEmit?.length ||
(buildInfo.program as ProgramMultiFileEmitBuildInfo).emitDiagnosticsPerFile?.length :
some((buildInfo.program as ProgramMultiFileEmitBuildInfo).semanticDiagnosticsPerFile, isArray))
(buildInfo.program as ProgramMultiFileEmitBuildInfo).semanticDiagnosticsPerFile?.length)
) {
return {
type: UpToDateStatusType.OutOfDateBuildInfo,
Expand Down
Loading

0 comments on commit 87918f5

Please sign in to comment.