Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minor cleanups in builder #17208

Merged
2 commits merged into from
Jul 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,7 @@ namespace ts {
/**
* Stable sort of an array. Elements equal to each other maintain their relative position in the array.
*/
export function stableSort<T>(array: ReadonlyArray<T>, comparer: (x: T, y: T) => Comparison = compareValues) {
export function stableSort<T>(array: ReadonlyArray<T>, comparer: Comparer<T> = compareValues) {
return array
.map((_, i) => i) // create array of indices
.sort((x, y) => comparer(array[x], array[y]) || compareValues(x, y)) // sort indices by value then position
Expand Down Expand Up @@ -852,14 +852,16 @@ namespace ts {
return result;
}

export type Comparer<T> = (a: T, b: T) => Comparison;

/**
* Performs a binary search, finding the index at which 'value' occurs in 'array'.
* If no such index is found, returns the 2's-complement of first index at which
* number[index] exceeds number.
* @param array A sorted array whose first element must be no larger than number
* @param number The value to be searched for in the array.
*/
export function binarySearch<T>(array: ReadonlyArray<T>, value: T, comparer?: (v1: T, v2: T) => number, offset?: number): number {
export function binarySearch<T>(array: ReadonlyArray<T>, value: T, comparer?: Comparer<T>, offset?: number): number {
if (!array || array.length === 0) {
return -1;
}
Expand Down
87 changes: 15 additions & 72 deletions src/server/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,57 +203,27 @@ namespace ts.server {
}

class ModuleBuilderFileInfo extends BuilderFileInfo {
references: ModuleBuilderFileInfo[] = [];
referencedBy: ModuleBuilderFileInfo[] = [];
references = createSortedArray<ModuleBuilderFileInfo>();
readonly referencedBy = createSortedArray<ModuleBuilderFileInfo>();
scriptVersionForReferences: string;

static compareFileInfos(lf: ModuleBuilderFileInfo, rf: ModuleBuilderFileInfo): number {
const l = lf.scriptInfo.fileName;
const r = rf.scriptInfo.fileName;
return (l < r ? -1 : (l > r ? 1 : 0));
}

static addToReferenceList(array: ModuleBuilderFileInfo[], fileInfo: ModuleBuilderFileInfo) {
if (array.length === 0) {
array.push(fileInfo);
return;
}

const insertIndex = binarySearch(array, fileInfo, ModuleBuilderFileInfo.compareFileInfos);
if (insertIndex < 0) {
array.splice(~insertIndex, 0, fileInfo);
}
}

static removeFromReferenceList(array: ModuleBuilderFileInfo[], fileInfo: ModuleBuilderFileInfo) {
if (!array || array.length === 0) {
return;
}

if (array[0] === fileInfo) {
array.splice(0, 1);
return;
}

const removeIndex = binarySearch(array, fileInfo, ModuleBuilderFileInfo.compareFileInfos);
if (removeIndex >= 0) {
array.splice(removeIndex, 1);
}
static compareFileInfos(lf: ModuleBuilderFileInfo, rf: ModuleBuilderFileInfo): Comparison {
return compareStrings(lf.scriptInfo.fileName, rf.scriptInfo.fileName);
}

addReferencedBy(fileInfo: ModuleBuilderFileInfo): void {
ModuleBuilderFileInfo.addToReferenceList(this.referencedBy, fileInfo);
insertSorted(this.referencedBy, fileInfo, ModuleBuilderFileInfo.compareFileInfos);
}

removeReferencedBy(fileInfo: ModuleBuilderFileInfo): void {
ModuleBuilderFileInfo.removeFromReferenceList(this.referencedBy, fileInfo);
removeSorted(this.referencedBy, fileInfo, ModuleBuilderFileInfo.compareFileInfos);
}

removeFileReferences() {
for (const reference of this.references) {
reference.removeReferencedBy(this);
}
this.references = [];
this.references = createSortedArray<ModuleBuilderFileInfo>();
}
}

Expand All @@ -270,16 +240,13 @@ namespace ts.server {
super.clear();
}

private getReferencedFileInfos(fileInfo: ModuleBuilderFileInfo): ModuleBuilderFileInfo[] {
private getReferencedFileInfos(fileInfo: ModuleBuilderFileInfo): SortedArray<ModuleBuilderFileInfo> {
if (!fileInfo.isExternalModuleOrHasOnlyAmbientExternalModules()) {
return [];
return createSortedArray();
}

const referencedFilePaths = this.project.getReferencedFiles(fileInfo.scriptInfo.path);
if (referencedFilePaths.length > 0) {
return map<Path, ModuleBuilderFileInfo>(referencedFilePaths, f => this.getOrCreateFileInfo(f)).sort(ModuleBuilderFileInfo.compareFileInfos);
}
return [];
return toSortedArray(referencedFilePaths.map(f => this.getOrCreateFileInfo(f)), ModuleBuilderFileInfo.compareFileInfos);
}

protected ensureFileInfoIfInProject(_scriptInfo: ScriptInfo) {
Expand Down Expand Up @@ -319,39 +286,15 @@ namespace ts.server {

const newReferences = this.getReferencedFileInfos(fileInfo);
const oldReferences = fileInfo.references;

let oldIndex = 0;
let newIndex = 0;
while (oldIndex < oldReferences.length && newIndex < newReferences.length) {
const oldReference = oldReferences[oldIndex];
const newReference = newReferences[newIndex];
const compare = ModuleBuilderFileInfo.compareFileInfos(oldReference, newReference);
if (compare < 0) {
enumerateInsertsAndDeletes(newReferences, oldReferences,
/*inserted*/ newReference => newReference.addReferencedBy(fileInfo),
/*deleted*/ oldReference => {
// New reference is greater then current reference. That means
// the current reference doesn't exist anymore after parsing. So delete
// references.
oldReference.removeReferencedBy(fileInfo);
oldIndex++;
}
else if (compare > 0) {
// A new reference info. Add it.
newReference.addReferencedBy(fileInfo);
newIndex++;
}
else {
// Equal. Go to next
oldIndex++;
newIndex++;
}
}
// Clean old references
for (let i = oldIndex; i < oldReferences.length; i++) {
oldReferences[i].removeReferencedBy(fileInfo);
}
// Update new references
for (let i = newIndex; i < newReferences.length; i++) {
newReferences[i].addReferencedBy(fileInfo);
}
},
/*compare*/ ModuleBuilderFileInfo.compareFileInfos);

fileInfo.references = newReferences;
fileInfo.scriptVersionForReferences = fileInfo.scriptInfo.getLatestVersion();
Expand Down
6 changes: 3 additions & 3 deletions src/server/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ namespace ts.server {
for (const sourceFile of this.program.getSourceFiles()) {
this.extractUnresolvedImportsFromSourceFile(sourceFile, result);
}
this.lastCachedUnresolvedImportsList = toSortedReadonlyArray(result);
this.lastCachedUnresolvedImportsList = toSortedArray(result);
}
unresolvedImports = this.lastCachedUnresolvedImportsList;

Expand Down Expand Up @@ -783,7 +783,7 @@ namespace ts.server {
// We need to use a set here since the code can contain the same import twice,
// but that will only be one dependency.
// To avoid invernal conversion, the key of the referencedFiles map must be of type Path
const referencedFiles = createMap<boolean>();
const referencedFiles = createMap<true>();
if (sourceFile.imports && sourceFile.imports.length > 0) {
const checker: TypeChecker = this.program.getTypeChecker();
for (const importName of sourceFile.imports) {
Expand Down Expand Up @@ -1057,7 +1057,7 @@ namespace ts.server {
}

getExternalFiles(): SortedReadonlyArray<string> {
return toSortedReadonlyArray(flatMap(this.plugins, plugin => {
return toSortedArray(flatMap(this.plugins, plugin => {
if (typeof plugin.getExternalFiles !== "function") return;
try {
return plugin.getExternalFiles(this);
Expand Down
6 changes: 5 additions & 1 deletion src/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ declare namespace ts.server {
require?(initialPath: string, moduleName: string): RequireResult;
}

export interface SortedArray<T> extends Array<T> {
" __sortedArrayBrand": any;
}

export interface SortedReadonlyArray<T> extends ReadonlyArray<T> {
" __sortedReadonlyArrayBrand": any;
" __sortedArrayBrand": any;
}

export interface TypingInstallerRequest {
Expand Down
2 changes: 1 addition & 1 deletion src/server/typingsCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ namespace ts.server {
this.perProjectCache.set(projectName, {
compilerOptions,
typeAcquisition,
typings: toSortedReadonlyArray(newTypings),
typings: toSortedArray(newTypings),
unresolvedImports,
poisoned: false
});
Expand Down
80 changes: 57 additions & 23 deletions src/server/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace ts.server {
verbose
}

export const emptyArray: ReadonlyArray<any> = [];
export const emptyArray: SortedReadonlyArray<never> = createSortedArray<never>();

export interface Logger {
close(): void;
Expand Down Expand Up @@ -190,39 +190,45 @@ namespace ts.server {
return `/dev/null/inferredProject${counter}*`;
}

export function toSortedReadonlyArray(arr: string[]): SortedReadonlyArray<string> {
arr.sort();
return <any>arr;
export function createSortedArray<T>(): SortedArray<T> {
return [] as SortedArray<T>;
}

export function enumerateInsertsAndDeletes<T>(a: SortedReadonlyArray<T>, b: SortedReadonlyArray<T>, inserted: (item: T) => void, deleted: (item: T) => void, compare?: (a: T, b: T) => Comparison) {
export function toSortedArray(arr: string[]): SortedArray<string>;
export function toSortedArray<T>(arr: T[], comparer: Comparer<T>): SortedArray<T>;
export function toSortedArray<T>(arr: T[], comparer?: Comparer<T>): SortedArray<T> {
arr.sort(comparer);
return arr as SortedArray<T>;
}

export function enumerateInsertsAndDeletes<T>(newItems: SortedReadonlyArray<T>, oldItems: SortedReadonlyArray<T>, inserted: (newItem: T) => void, deleted: (oldItem: T) => void, compare?: Comparer<T>) {
compare = compare || compareValues;
let aIndex = 0;
let bIndex = 0;
const aLen = a.length;
const bLen = b.length;
while (aIndex < aLen && bIndex < bLen) {
const aItem = a[aIndex];
const bItem = b[bIndex];
const compareResult = compare(aItem, bItem);
let newIndex = 0;
let oldIndex = 0;
const newLen = newItems.length;
const oldLen = oldItems.length;
while (newIndex < newLen && oldIndex < oldLen) {
const newItem = newItems[newIndex];
const oldItem = oldItems[oldIndex];
const compareResult = compare(newItem, oldItem);
if (compareResult === Comparison.LessThan) {
inserted(aItem);
aIndex++;
inserted(newItem);
newIndex++;
}
else if (compareResult === Comparison.GreaterThan) {
deleted(bItem);
bIndex++;
deleted(oldItem);
oldIndex++;
}
else {
aIndex++;
bIndex++;
newIndex++;
oldIndex++;
}
}
while (aIndex < aLen) {
inserted(a[aIndex++]);
while (newIndex < newLen) {
inserted(newItems[newIndex++]);
}
while (bIndex < bLen) {
deleted(b[bIndex++]);
while (oldIndex < oldLen) {
deleted(oldItems[oldIndex++]);
}
}

Expand Down Expand Up @@ -273,4 +279,32 @@ namespace ts.server {
}
}
}

export function insertSorted<T>(array: SortedArray<T>, insert: T, compare: Comparer<T>): void {
if (array.length === 0) {
array.push(insert);
return;
}

const insertIndex = binarySearch(array, insert, compare);
if (insertIndex < 0) {
array.splice(~insertIndex, 0, insert);
}
}

export function removeSorted<T>(array: SortedArray<T>, remove: T, compare: Comparer<T>): void {
if (!array || array.length === 0) {
return;
}

if (array[0] === remove) {
array.splice(0, 1);
return;
}

const removeIndex = binarySearch(array, remove, compare);
if (removeIndex >= 0) {
array.splice(removeIndex, 1);
}
}
}