Skip to content

Commit

Permalink
Only copy emit state fields when backing up to restore if emit fails
Browse files Browse the repository at this point in the history
  • Loading branch information
sheetalkamat committed Apr 21, 2022
1 parent 21a6806 commit b32d2eb
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 63 deletions.
95 changes: 54 additions & 41 deletions src/compiler/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,18 @@ namespace ts {
filesChangingSignature?: Set<Path>;
}

type BackupEmitState = Pick<BuilderProgramState,
"affectedFilesPendingEmit" |
"affectedFilesPendingEmitIndex" |
"affectedFilesPendingEmitKind" |
"seenEmittedFiles" |
"programEmitComplete" |
"emitSignatures" |
"outSignature" |
"dtsChangeTime" |
"hasChangedEmitSignature"
> & { changedFilesSet: BuilderProgramState["changedFilesSet"] | undefined };

function hasSameKeys(map1: ReadonlyCollection<string> | undefined, map2: ReadonlyCollection<string> | undefined): boolean {
// Has same size and every key is present in both maps
return map1 === map2 || map1 !== undefined && map2 !== undefined && map1.size === map2.size && !forEachKey(map1, key => !map2.has(key));
Expand Down Expand Up @@ -296,34 +308,35 @@ namespace ts {
state.program = undefined;
}

/**
* Creates a clone of the state
*/
function cloneBuilderProgramState(state: Readonly<BuilderProgramState>): BuilderProgramState {
const newState = BuilderState.clone(state) as BuilderProgramState;
newState.semanticDiagnosticsPerFile = state.semanticDiagnosticsPerFile && new Map(state.semanticDiagnosticsPerFile);
newState.changedFilesSet = new Set(state.changedFilesSet);
newState.affectedFiles = state.affectedFiles;
newState.affectedFilesIndex = state.affectedFilesIndex;
newState.currentChangedFilePath = state.currentChangedFilePath;
newState.currentAffectedFilesSignatures = state.currentAffectedFilesSignatures && new Map(state.currentAffectedFilesSignatures);
newState.currentAffectedFilesExportedModulesMap = state.currentAffectedFilesExportedModulesMap?.clone();
newState.seenAffectedFiles = state.seenAffectedFiles && new Set(state.seenAffectedFiles);
newState.cleanedDiagnosticsOfLibFiles = state.cleanedDiagnosticsOfLibFiles;
newState.semanticDiagnosticsFromOldState = state.semanticDiagnosticsFromOldState && new Set(state.semanticDiagnosticsFromOldState);
newState.program = state.program;
newState.compilerOptions = state.compilerOptions;
newState.affectedFilesPendingEmit = state.affectedFilesPendingEmit && state.affectedFilesPendingEmit.slice();
newState.affectedFilesPendingEmitKind = state.affectedFilesPendingEmitKind && new Map(state.affectedFilesPendingEmitKind);
newState.affectedFilesPendingEmitIndex = state.affectedFilesPendingEmitIndex;
newState.seenEmittedFiles = state.seenEmittedFiles && new Map(state.seenEmittedFiles);
newState.programEmitComplete = state.programEmitComplete;
newState.filesChangingSignature = state.filesChangingSignature && new Set(state.filesChangingSignature);
newState.emitSignatures = state.emitSignatures && new Map(state.emitSignatures);
newState.outSignature = state.outSignature;
newState.dtsChangeTime = state.dtsChangeTime;
newState.hasChangedEmitSignature = state.hasChangedEmitSignature;
return newState;
function backupEmitBuilderProgramState(state: Readonly<BuilderProgramState>): BackupEmitState {
const outFilePath = outFile(state.compilerOptions);
// Only in --out changeFileSet is kept around till emit
Debug.assert(!state.changedFilesSet.size || outFilePath);
return {
affectedFilesPendingEmit: state.affectedFilesPendingEmit && state.affectedFilesPendingEmit.slice(),
affectedFilesPendingEmitKind: state.affectedFilesPendingEmitKind && new Map(state.affectedFilesPendingEmitKind),
affectedFilesPendingEmitIndex: state.affectedFilesPendingEmitIndex,
seenEmittedFiles: state.seenEmittedFiles && new Map(state.seenEmittedFiles),
programEmitComplete: state.programEmitComplete,
emitSignatures: state.emitSignatures && new Map(state.emitSignatures),
outSignature: state.outSignature,
dtsChangeTime: state.dtsChangeTime,
hasChangedEmitSignature: state.hasChangedEmitSignature,
changedFilesSet: outFilePath ? new Set(state.changedFilesSet) : undefined,
};
}

function restoreEmitBuilderProgramState(state: BuilderProgramState, backupEmitState: BackupEmitState) {
state.affectedFilesPendingEmit = backupEmitState.affectedFilesPendingEmit;
state.affectedFilesPendingEmitKind = backupEmitState.affectedFilesPendingEmitKind;
state.affectedFilesPendingEmitIndex = backupEmitState.affectedFilesPendingEmitIndex;
state.seenEmittedFiles = backupEmitState.seenEmittedFiles;
state.programEmitComplete = backupEmitState.programEmitComplete;
state.emitSignatures = backupEmitState.emitSignatures;
state.outSignature = backupEmitState.outSignature;
state.dtsChangeTime = backupEmitState.dtsChangeTime;
state.hasChangedEmitSignature = backupEmitState.hasChangedEmitSignature;
if (backupEmitState.changedFilesSet) state.changedFilesSet = backupEmitState.changedFilesSet;
}

/**
Expand Down Expand Up @@ -1090,8 +1103,8 @@ namespace ts {
* Computing hash to for signature verification
*/
const computeHash = maybeBind(host, host.createHash);
let state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState, host.disableUseFileVersionAsSignature);
let backupState: BuilderProgramState | undefined;
const state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState, host.disableUseFileVersionAsSignature);
let backupEmitState: BackupEmitState | undefined;
newProgram.getProgramBuildInfo = () => getProgramBuildInfo(state, getCanonicalFileName, host);

// To ensure that we arent storing any references to old program or new program without state
Expand All @@ -1102,20 +1115,20 @@ namespace ts {
const getState = () => state;
const builderProgram = createRedirectedBuilderProgram(getState, configFileParsingDiagnostics);
builderProgram.getState = getState;
builderProgram.backupState = () => {
Debug.assert(backupState === undefined);
backupState = cloneBuilderProgramState(state);
builderProgram.backupEmitState = () => {
Debug.assert(backupEmitState === undefined);
backupEmitState = backupEmitBuilderProgramState(state);
};
builderProgram.restoreState = () => {
state = Debug.checkDefined(backupState);
backupState = undefined;
builderProgram.restoreEmitState = () => {
restoreEmitBuilderProgramState(state, Debug.checkDefined(backupEmitState));
backupEmitState = undefined;
};
builderProgram.getAllDependencies = sourceFile => BuilderState.getAllDependencies(state, Debug.checkDefined(state.program), sourceFile);
builderProgram.getSemanticDiagnostics = getSemanticDiagnostics;
builderProgram.emit = emit;
builderProgram.releaseProgram = () => {
releaseCache(state);
backupState = undefined;
backupEmitState = undefined;
};

if (kind === BuilderProgramKind.SemanticDiagnosticsBuilderProgram) {
Expand Down Expand Up @@ -1451,8 +1464,8 @@ namespace ts {
};
return {
getState: () => state,
backupState: noop,
restoreState: noop,
backupEmitState: noop,
restoreEmitState: noop,
getProgram: notImplemented,
getProgramOrUndefined: returnUndefined,
releaseProgram: noop,
Expand Down Expand Up @@ -1506,8 +1519,8 @@ namespace ts {
export function createRedirectedBuilderProgram(getState: () => { program?: Program | undefined; compilerOptions: CompilerOptions; }, configFileParsingDiagnostics: readonly Diagnostic[]): BuilderProgram {
return {
getState: notImplemented,
backupState: noop,
restoreState: noop,
backupEmitState: noop,
restoreEmitState: noop,
getProgram,
getProgramOrUndefined: () => getState().program,
releaseProgram: () => getState().program = undefined,
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/builderPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ namespace ts {
/*@internal*/
getState(): ReusableBuilderProgramState;
/*@internal*/
backupState(): void;
backupEmitState(): void;
/*@internal*/
restoreState(): void;
restoreEmitState(): void;
/**
* Returns current program
*/
Expand Down
16 changes: 0 additions & 16 deletions src/compiler/builderState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ namespace ts {
}

export interface ReadonlyManyToManyPathMap {
clone(): ManyToManyPathMap;
forEach(action: (v: ReadonlySet<Path>, k: Path) => void): void;
getKeys(v: Path): ReadonlySet<Path> | undefined;
getValues(k: Path): ReadonlySet<Path> | undefined;
Expand All @@ -85,7 +84,6 @@ namespace ts {
export function createManyToManyPathMap(): ManyToManyPathMap {
function create(forward: ESMap<Path, ReadonlySet<Path>>, reverse: ESMap<Path, Set<Path>>, deleted: Set<Path> | undefined): ManyToManyPathMap {
const map: ManyToManyPathMap = {
clone: () => create(new Map(forward), new Map(reverse), deleted && new Set(deleted)),
forEach: fn => forward.forEach(fn),
getKeys: v => reverse.get(v),
getValues: k => forward.get(k),
Expand Down Expand Up @@ -324,20 +322,6 @@ namespace ts {
state.allFileNames = undefined;
}

/**
* Creates a clone of the state
*/
export function clone(state: Readonly<BuilderState>): BuilderState {
// Dont need to backup allFiles info since its cache anyway
return {
fileInfos: new Map(state.fileInfos),
referencedMap: state.referencedMap?.clone(),
exportedModulesMap: state.exportedModulesMap?.clone(),
hasCalledUpdateShapeSignature: state.hasCalledUpdateShapeSignature && new Set(state.hasCalledUpdateShapeSignature),
useFileVersionAsSignature: state.useFileVersionAsSignature,
};
}

/**
* Gets the files affected by the path from the program
*/
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/tsbuildPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ namespace ts {
Debug.assertIsDefined(program);
Debug.assert(step === BuildStep.Emit);
// Before emitting lets backup state, so we can revert it back if there are declaration errors to handle emit and declaration errors correctly
program.backupState();
program.backupEmitState();
let declDiagnostics: Diagnostic[] | undefined;
const reportDeclarationDiagnostics = (d: Diagnostic) => (declDiagnostics || (declDiagnostics = [])).push(d);
const outputFiles: OutputFile[] = [];
Expand All @@ -964,7 +964,7 @@ namespace ts {
);
// Don't emit .d.ts if there are decl file errors
if (declDiagnostics) {
program.restoreState();
program.restoreEmitState();
({ buildResult, step } = buildErrors(
state,
projectPath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Semantic diagnostics in builder refreshed for::
Shape signatures in builder refreshed for::
/a/lib/lib.d.ts (used version)
/user/username/projects/solution/app/filewitherror.ts (used version)
/user/username/projects/solution/app/filewithouterror.ts (computed .d.ts)
/user/username/projects/solution/app/filewithouterror.ts (computed .d.ts during emit)

WatchedFiles::
/user/username/projects/solution/app/tsconfig.json:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Semantic diagnostics in builder refreshed for::
Shape signatures in builder refreshed for::
/a/lib/lib.d.ts (used version)
/user/username/projects/solution/app/filewitherror.ts (used version)
/user/username/projects/solution/app/filewithouterror.ts (computed .d.ts)
/user/username/projects/solution/app/filewithouterror.ts (computed .d.ts during emit)

WatchedFiles::
/user/username/projects/solution/app/tsconfig.json:
Expand Down

0 comments on commit b32d2eb

Please sign in to comment.