Skip to content

Commit

Permalink
Structure is reused should be on new program instead of old program (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
sheetalkamat committed Oct 9, 2020
1 parent 876e44b commit e6d525c
Show file tree
Hide file tree
Showing 220 changed files with 645 additions and 67 deletions.
61 changes: 31 additions & 30 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -824,9 +824,9 @@ namespace ts {
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
// We set `structuralIsReused` to `undefined` because `tryReuseStructureFromOldProgram` calls `tryReuseStructureFromOldProgram` which checks
// `structuralIsReused`, which would be a TDZ violation if it was not set in advance to `undefined`.
let structuralIsReused: StructureIsReused | undefined;
structuralIsReused = tryReuseStructureFromOldProgram(); // eslint-disable-line prefer-const
if (structuralIsReused !== StructureIsReused.Completely) {
let structureIsReused: StructureIsReused;
structureIsReused = tryReuseStructureFromOldProgram(); // eslint-disable-line prefer-const
if (structureIsReused !== StructureIsReused.Completely) {
processingDefaultLibFiles = [];
processingOtherFiles = [];

Expand Down Expand Up @@ -979,6 +979,7 @@ namespace ts {
getSymlinkCache,
realpath: host.realpath?.bind(host),
useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
structureIsReused,
};

onProgramCreateComplete();
Expand Down Expand Up @@ -1107,7 +1108,7 @@ namespace ts {
}

function resolveModuleNamesReusingOldState(moduleNames: string[], file: SourceFile): readonly ResolvedModuleFull[] {
if (structuralIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) {
if (structureIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) {
// If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
// the best we can do is fallback to the default logic.
return resolveModuleNamesWorker(moduleNames, file, /*reusedNames*/ undefined);
Expand Down Expand Up @@ -1278,22 +1279,22 @@ namespace ts {
// if any of these properties has changed - structure cannot be reused
const oldOptions = oldProgram.getCompilerOptions();
if (changesAffectModuleResolution(oldOptions, options)) {
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}

// there is an old program, check if we can reuse its structure
const oldRootNames = oldProgram.getRootFileNames();
if (!arrayIsEqualTo(oldRootNames, rootNames)) {
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}

if (!arrayIsEqualTo(options.types, oldOptions.types)) {
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}

// Check if any referenced project tsconfig files are different
if (!canReuseProjectReferences()) {
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}
if (projectReferences) {
resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
Expand All @@ -1302,13 +1303,13 @@ namespace ts {
// check if program source files has changed in the way that can affect structure of the program
const newSourceFiles: SourceFile[] = [];
const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = [];
oldProgram.structureIsReused = StructureIsReused.Completely;
structureIsReused = StructureIsReused.Completely;

// If the missing file paths are now present, it can change the progam structure,
// and hence cant reuse the structure.
// This is same as how we dont reuse the structure if one of the file from old program is now missing
if (oldProgram.getMissingFilePaths().some(missingFilePath => host.fileExists(missingFilePath))) {
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}

const oldSourceFiles = oldProgram.getSourceFiles();
Expand All @@ -1321,7 +1322,7 @@ namespace ts {
: host.getSourceFile(oldSourceFile.fileName, options.target!, /*onError*/ undefined, shouldCreateNewSourceFile); // TODO: GH#18217

if (!newSourceFile) {
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}

Debug.assert(!newSourceFile.redirectInfo, "Host should not return a redirect source file from `getSourceFile`");
Expand All @@ -1332,15 +1333,15 @@ namespace ts {
// This lets us know if the unredirected file has changed. If it has we should break the redirect.
if (newSourceFile !== oldSourceFile.redirectInfo.unredirected) {
// Underlying file has changed. Might not redirect anymore. Must rebuild program.
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}
fileChanged = false;
newSourceFile = oldSourceFile; // Use the redirect.
}
else if (oldProgram.redirectTargetsMap.has(oldSourceFile.path)) {
// If a redirected-to source file changes, the redirect may be broken.
if (newSourceFile !== oldSourceFile) {
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}
fileChanged = false;
}
Expand All @@ -1361,7 +1362,7 @@ namespace ts {
const prevKind = seenPackageNames.get(packageName);
const newKind = fileChanged ? SeenPackageName.Modified : SeenPackageName.Exists;
if ((prevKind !== undefined && newKind === SeenPackageName.Modified) || prevKind === SeenPackageName.Modified) {
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}
seenPackageNames.set(packageName, newKind);
}
Expand All @@ -1371,47 +1372,47 @@ namespace ts {

if (!arrayIsEqualTo(oldSourceFile.libReferenceDirectives, newSourceFile.libReferenceDirectives, fileReferenceIsEqualTo)) {
// 'lib' references has changed. Matches behavior in changesAffectModuleResolution
return oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
}

if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) {
// value of no-default-lib has changed
// this will affect if default library is injected into the list of files
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;
}

// check tripleslash references
if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) {
// tripleslash references has changed
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;
}

// check imports and module augmentations
collectExternalModuleReferences(newSourceFile);
if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
// imports has changed
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;
}
if (!arrayIsEqualTo(oldSourceFile.moduleAugmentations, newSourceFile.moduleAugmentations, moduleNameIsEqualTo)) {
// moduleAugmentations has changed
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;
}
if ((oldSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags) !== (newSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags)) {
// dynamicImport has changed
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;
}

if (!arrayIsEqualTo(oldSourceFile.typeReferenceDirectives, newSourceFile.typeReferenceDirectives, fileReferenceIsEqualTo)) {
// 'types' references has changed
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;
}

// tentatively approve the file
modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
}
else if (hasInvalidatedResolution(oldSourceFile.path)) {
// 'module/types' references could have changed
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;

// add file to the modified list so that we will resolve it later
modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
Expand All @@ -1421,8 +1422,8 @@ namespace ts {
newSourceFiles.push(newSourceFile);
}

if (oldProgram.structureIsReused !== StructureIsReused.Completely) {
return oldProgram.structureIsReused;
if (structureIsReused !== StructureIsReused.Completely) {
return structureIsReused;
}

const modifiedFiles = modifiedSourceFiles.map(f => f.oldFile);
Expand All @@ -1440,7 +1441,7 @@ namespace ts {
// ensure that module resolution results are still correct
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
if (resolutionsChanged) {
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;
newSourceFile.resolvedModules = zipToMap(moduleNames, resolutions);
}
else {
Expand All @@ -1452,20 +1453,20 @@ namespace ts {
// ensure that types resolutions are still correct
const typeReferenceEesolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, typeReferenceResolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
if (typeReferenceEesolutionsChanged) {
oldProgram.structureIsReused = StructureIsReused.SafeModules;
structureIsReused = StructureIsReused.SafeModules;
newSourceFile.resolvedTypeReferenceDirectiveNames = zipToMap(typesReferenceDirectives, typeReferenceResolutions);
}
else {
newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
}
}

if (oldProgram.structureIsReused !== StructureIsReused.Completely) {
return oldProgram.structureIsReused;
if (structureIsReused !== StructureIsReused.Completely) {
return structureIsReused;
}

if (host.hasChangedAutomaticTypeDirectiveNames?.()) {
return oldProgram.structureIsReused = StructureIsReused.SafeModules;
return StructureIsReused.SafeModules;
}

missingFilePaths = oldProgram.getMissingFilePaths();
Expand Down Expand Up @@ -1503,7 +1504,7 @@ namespace ts {
sourceFileToPackageName = oldProgram.sourceFileToPackageName;
redirectTargetsMap = oldProgram.redirectTargetsMap;

return oldProgram.structureIsReused = StructureIsReused.Completely;
return StructureIsReused.Completely;
}

function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost {
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3779,7 +3779,8 @@ namespace ts {
isSourceFileDefaultLibrary(file: SourceFile): boolean;

// For testing purposes only.
/* @internal */ structureIsReused?: StructureIsReused;
// This is set on created program to let us know how the program was created using old program
/* @internal */ readonly structureIsReused: StructureIsReused;

/* @internal */ getSourceFileFromReference(referencingFile: SourceFile | UnparsedSource, ref: FileReference): SourceFile | undefined;
/* @internal */ getLibFileFromReference(ref: FileReference): SourceFile | undefined;
Expand Down
6 changes: 3 additions & 3 deletions src/server/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1086,7 +1086,7 @@ namespace ts.server {
// bump up the version if
// - oldProgram is not set - this is a first time updateGraph is called
// - newProgram is different from the old program and structure of the old program was not reused.
const hasNewProgram = this.program && (!oldProgram || (this.program !== oldProgram && !(oldProgram.structureIsReused! & StructureIsReused.Completely)));
const hasNewProgram = this.program && (!oldProgram || (this.program !== oldProgram && !(this.program.structureIsReused & StructureIsReused.Completely)));
if (hasNewProgram) {
if (oldProgram) {
for (const f of oldProgram.getSourceFiles()) {
Expand Down Expand Up @@ -1153,7 +1153,7 @@ namespace ts.server {
}

if (!this.importSuggestionsCache.isEmpty()) {
if (this.hasAddedorRemovedFiles || oldProgram && !oldProgram.structureIsReused) {
if (this.hasAddedorRemovedFiles || oldProgram && !this.program.structureIsReused) {
this.importSuggestionsCache.clear();
}
else if (this.dirtyFilesForSuggestions && oldProgram && this.program) {
Expand Down Expand Up @@ -1194,7 +1194,7 @@ namespace ts.server {
this.print(/*writeProjectFileNames*/ true);
}
else if (this.program !== oldProgram) {
this.writeLog(`Different program with same set of files:: oldProgram.structureIsReused:: ${oldProgram && oldProgram.structureIsReused}`);
this.writeLog(`Different program with same set of files:: structureIsReused:: ${this.program.structureIsReused}`);
}
return hasNewProgram;
}
Expand Down
4 changes: 2 additions & 2 deletions src/testRunner/unittests/moduleResolution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1387,8 +1387,8 @@ import b = require("./moduleB");
const diagnostics1 = program1.getFileProcessingDiagnostics().getDiagnostics();
assert.equal(diagnostics1.length, 1, "expected one diagnostic");

createProgram(names, {}, compilerHost, program1);
assert.isTrue(program1.structureIsReused === StructureIsReused.Completely);
const program2 = createProgram(names, {}, compilerHost, program1);
assert.isTrue(program2.structureIsReused === StructureIsReused.Completely);
const diagnostics2 = program1.getFileProcessingDiagnostics().getDiagnostics();
assert.equal(diagnostics2.length, 1, "expected one diagnostic");
assert.equal(diagnostics1[0].messageText, diagnostics2[0].messageText, "expected one diagnostic");
Expand Down

0 comments on commit e6d525c

Please sign in to comment.