From f5ad79fe7a29a23e5784125019d99cccbaa05da4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 16 Dec 2014 14:12:17 -0800 Subject: [PATCH] Remove the dependency that TypeChecker and Emitter have on Program. Instead, these layers explicitly specify the functionality they need, and don't take in anything extra. --- src/compiler/checker.ts | 37 ++++++++++++------------------------- src/compiler/emitter.ts | 6 +++--- src/compiler/program.ts | 27 +++++++++++++++++++++------ src/compiler/tsc.ts | 2 +- src/compiler/types.ts | 32 ++++++++++++++++++++++++++++++-- src/harness/harness.ts | 2 +- 6 files changed, 68 insertions(+), 38 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b01c2df68ee..95f392f036b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9,7 +9,7 @@ module ts { /// If fullTypeCheck === true, then the typechecker should do every possible check to produce all errors /// If fullTypeCheck === false, the typechecker can take shortcuts and skip checks that only produce errors. /// NOTE: checks that somehow affect decisions being made during typechecking should be executed in both cases. - export function createTypeChecker(program: Program, produceDiagnostics: boolean): TypeChecker { + export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker { var Symbol = objectAllocator.getSymbolConstructor(); var Type = objectAllocator.getTypeConstructor(); var Signature = objectAllocator.getSignatureConstructor(); @@ -19,13 +19,13 @@ module ts { var emptyArray: any[] = []; var emptySymbols: SymbolTable = {}; - var compilerOptions = program.getCompilerOptions(); + var compilerOptions = host.getCompilerOptions(); var emitResolver = createResolver(); var checker: TypeChecker = { - getNodeCount: () => sum(program.getSourceFiles(), "nodeCount"), - getIdentifierCount: () => sum(program.getSourceFiles(), "identifierCount"), - getSymbolCount: () => sum(program.getSourceFiles(), "symbolCount"), + getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"), + getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"), + getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount"), getTypeCount: () => typeCount, isUndefinedSymbol: symbol => symbol === undefinedSymbol, isArgumentsSymbol: symbol => symbol === argumentsSymbol, @@ -55,8 +55,6 @@ module ts { getSignatureFromDeclaration, isImplementationOfOverload, getAliasedSymbol: resolveImport, - hasEarlyErrors, - isEmitBlocked, getEmitResolver: () => emitResolver, }; @@ -277,7 +275,7 @@ module ts { return true; } - var sourceFiles = program.getSourceFiles(); + var sourceFiles = host.getSourceFiles(); return sourceFiles.indexOf(file1) <= sourceFiles.indexOf(file2); } @@ -517,7 +515,7 @@ module ts { } while (true) { var filename = normalizePath(combinePaths(searchPath, moduleName)); - var sourceFile = program.getSourceFile(filename + ".ts") || program.getSourceFile(filename + ".d.ts"); + var sourceFile = host.getSourceFile(filename + ".ts") || host.getSourceFile(filename + ".d.ts"); if (sourceFile || isRelative) break; var parentPath = getDirectoryPath(searchPath); if (parentPath === searchPath) break; @@ -3394,7 +3392,7 @@ module ts { if (containingMessageChain) { errorInfo = concatenateDiagnosticMessageChains(containingMessageChain, errorInfo); } - addDiagnostic(createDiagnosticForNodeFromMessageChain(errorNode, errorInfo, program.getCompilerHost().getNewLine())); + addDiagnostic(createDiagnosticForNodeFromMessageChain(errorNode, errorInfo, host.getCompilerHost().getNewLine())); } return result !== Ternary.False; @@ -8323,7 +8321,7 @@ module ts { var errorInfo = chainDiagnosticMessages(undefined, Diagnostics.Named_properties_0_of_types_1_and_2_are_not_identical, prop.name, typeName1, typeName2); errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Interface_0_cannot_simultaneously_extend_types_1_and_2, typeToString(type), typeName1, typeName2); - addDiagnostic(createDiagnosticForNodeFromMessageChain(typeNode, errorInfo, program.getCompilerHost().getNewLine())); + addDiagnostic(createDiagnosticForNodeFromMessageChain(typeNode, errorInfo, host.getCompilerHost().getNewLine())); } } } @@ -8934,7 +8932,7 @@ module ts { checkSourceFile(sourceFile); return filter(getSortedDiagnostics(), d => d.file === sourceFile); } - forEach(program.getSourceFiles(), checkSourceFile); + forEach(host.getSourceFiles(), checkSourceFile); return getSortedDiagnostics(); } @@ -9451,16 +9449,6 @@ module ts { return getDiagnostics(sourceFile).length > 0 || getGlobalDiagnostics().length > 0; } - function isEmitBlocked(sourceFile?: SourceFile): boolean { - return program.getDiagnostics(sourceFile).length !== 0 || - hasEarlyErrors(sourceFile) || - (compilerOptions.noEmitOnError && getDiagnostics(sourceFile).length !== 0); - } - - function hasEarlyErrors(sourceFile?: SourceFile): boolean { - return forEach(getDiagnostics(sourceFile), d => d.isEarly); - } - function isImportResolvedToValue(symbol: Symbol): boolean { var target = resolveImport(symbol); // const enums and modules that contain only const enums are not considered values from the emit perespective @@ -9556,7 +9544,6 @@ module ts { getEnumMemberValue, isTopLevelValueImportWithEntityName, hasSemanticErrors, - isEmitBlocked, isDeclarationVisible, isImplementationOfOverload, writeTypeOfDeclaration, @@ -9570,12 +9557,12 @@ module ts { function initializeTypeChecker() { // Bind all source files and propagate errors - forEach(program.getSourceFiles(), file => { + forEach(host.getSourceFiles(), file => { bindSourceFile(file); forEach(file.semanticDiagnostics, addDiagnostic); }); // Initialize global symbol table - forEach(program.getSourceFiles(), file => { + forEach(host.getSourceFiles(), file => { if (!isExternalModule(file)) { extendSymbolTable(globals, file.locals); } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index b18881684d4..7b20fb8e216 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -4138,7 +4138,7 @@ module ts { if (targetSourceFile === undefined) { // No targetSourceFile is specified (e.g. calling emitter from batch compiler) hasSemanticErrors = resolver.hasSemanticErrors(); - isEmitBlocked = resolver.isEmitBlocked(); + isEmitBlocked = host.isEmitBlocked(); forEach(host.getSourceFiles(), sourceFile => { if (shouldEmitToOwnFile(sourceFile, compilerOptions)) { @@ -4156,7 +4156,7 @@ module ts { if (shouldEmitToOwnFile(targetSourceFile, compilerOptions)) { // If shouldEmitToOwnFile returns true or targetSourceFile is an external module file, then emit targetSourceFile in its own output file hasSemanticErrors = resolver.hasSemanticErrors(targetSourceFile); - isEmitBlocked = resolver.isEmitBlocked(targetSourceFile); + isEmitBlocked = host.isEmitBlocked(targetSourceFile); var jsFilePath = getOwnEmitOutputFilePath(targetSourceFile, host, ".js"); emitFile(jsFilePath, targetSourceFile); @@ -4167,7 +4167,7 @@ module ts { forEach(host.getSourceFiles(), sourceFile => { if (!shouldEmitToOwnFile(sourceFile, compilerOptions)) { hasSemanticErrors = hasSemanticErrors || resolver.hasSemanticErrors(sourceFile); - isEmitBlocked = isEmitBlocked || resolver.isEmitBlocked(sourceFile); + isEmitBlocked = isEmitBlocked || host.isEmitBlocked(sourceFile); } }); diff --git a/src/compiler/program.ts b/src/compiler/program.ts index b1537da21c9..8d81ec45679 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -102,13 +102,28 @@ module ts { getDeclarationDiagnostics: getDeclarationDiagnostics, getTypeChecker, getCommonSourceDirectory: () => commonSourceDirectory, - emitFiles: invokeEmitter + emitFiles: invokeEmitter, + isEmitBlocked, }; return program; + function hasEarlyErrors(sourceFile?: SourceFile): boolean { + return forEach(getDiagnosticsProducingTypeChecker().getDiagnostics(sourceFile), d => d.isEarly); + } + + function isEmitBlocked(sourceFile?: SourceFile): boolean { + return getDiagnostics(sourceFile).length !== 0 || + hasEarlyErrors(sourceFile) || + (options.noEmitOnError && getDiagnosticsProducingTypeChecker().getDiagnostics(sourceFile).length !== 0); + } + + function getDiagnosticsProducingTypeChecker() { + return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ true)); + } + function getTypeChecker(produceDiagnostics: boolean) { if (produceDiagnostics) { - return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, produceDiagnostics)); + return getDiagnosticsProducingTypeChecker(); } else { return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, produceDiagnostics)); @@ -116,14 +131,14 @@ module ts { } function getDeclarationDiagnostics(targetSourceFile: SourceFile): Diagnostic[]{ - var fullTypeChecker = getTypeChecker(/*produceDiagnostics:*/true); - fullTypeChecker.getDiagnostics(targetSourceFile); - var resolver = fullTypeChecker.getEmitResolver(); + var typeChecker = getDiagnosticsProducingTypeChecker(); + typeChecker.getDiagnostics(targetSourceFile); + var resolver = typeChecker.getEmitResolver(); return ts.getDeclarationDiagnostics(program, resolver, targetSourceFile); } function invokeEmitter(targetSourceFile?: SourceFile) { - var resolver = getTypeChecker(/*produceDiagnostics:*/true).getEmitResolver(); + var resolver = getDiagnosticsProducingTypeChecker().getEmitResolver(); return emitFiles(resolver, program, targetSourceFile); } diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index d15ad44ca1c..0371b3f97cf 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -286,7 +286,7 @@ module ts { var checker = program.getTypeChecker(/*fullTypeCheckMode*/ true); var checkStart = new Date().getTime(); errors = checker.getDiagnostics(); - if (checker.isEmitBlocked()) { + if (program.isEmitBlocked()) { exitStatus = EmitReturnStatus.AllOutputGenerationSkipped; } else { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f8cf9a8c838..0a00dff6d39 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -926,7 +926,9 @@ module ts { // will throw an invalid operation exception. getTypeChecker(produceDiagnostics: boolean): TypeChecker; getCommonSourceDirectory(): string; + emitFiles(targetSourceFile?: SourceFile): EmitResult; + isEmitBlocked(sourceFile?: SourceFile): boolean; } export interface SourceMapSpan { @@ -966,6 +968,32 @@ module ts { sourceMaps: SourceMapData[]; // Array of sourceMapData if compiler emitted sourcemaps } + export interface TypeCheckerHost { + getCompilerOptions(): CompilerOptions; + getCompilerHost(): CompilerHost; + + getSourceFiles(): SourceFile[]; + getSourceFile(filename: string): SourceFile; + //getSourceFiles(): SourceFile[]; + //getCompilerOptions(): CompilerOptions; + + //getDiagnostics(sourceFile?: SourceFile): Diagnostic[]; + //getGlobalDiagnostics(): Diagnostic[]; + //getDeclarationDiagnostics(sourceFile: SourceFile): Diagnostic[]; + + //// Gets a type checker that can be used to semantically analyze source fils in the program. + //// The 'produceDiagnostics' flag determines if the checker will produce diagnostics while + //// analyzing the code. It can be set to 'false' to make many type checking operaitons + //// faster. With this flag set, the checker can avoid codepaths only necessary to produce + //// diagnostics, but not necessary to answer semantic questions about the code. + //// + //// If 'produceDiagnostics' is false, then any calls to get diagnostics from the TypeChecker + //// will throw an invalid operation exception. + //getTypeChecker(produceDiagnostics: boolean): TypeChecker; + //getCommonSourceDirectory(): string; + //emitFiles(targetSourceFile?: SourceFile): EmitResult; + } + export interface TypeChecker { getEmitResolver(): EmitResolver; getDiagnostics(sourceFile?: SourceFile): Diagnostic[]; @@ -997,7 +1025,7 @@ module ts { isImplementationOfOverload(node: FunctionLikeDeclaration): boolean; isUndefinedSymbol(symbol: Symbol): boolean; isArgumentsSymbol(symbol: Symbol): boolean; - isEmitBlocked(sourceFile?: SourceFile): boolean; + // Returns the constant value of this enum member, or 'undefined' if the enum member has a computed value. getEnumMemberValue(node: EnumMember): number; isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean; @@ -1080,6 +1108,7 @@ module ts { getCompilerHost(): CompilerHost; getCompilerOptions(): CompilerOptions; getCommonSourceDirectory(): string; + isEmitBlocked(sourceFile?: SourceFile): boolean; } export interface EmitResolver { @@ -1099,7 +1128,6 @@ module ts { isEntityNameVisible(entityName: EntityName, enclosingDeclaration: Node): SymbolVisibilityResult; // Returns the constant value this property access resolves to, or 'undefined' for a non-constant getConstantValue(node: PropertyAccessExpression | ElementAccessExpression): number; - isEmitBlocked(sourceFile?: SourceFile): boolean; isUnknownIdentifier(location: Node, name: string): boolean; } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 0696003c82c..e16d165c7a1 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1046,7 +1046,7 @@ module Harness { var checker = program.getTypeChecker(/*produceDiagnostics*/ true); - var isEmitBlocked = checker.isEmitBlocked(); + var isEmitBlocked = program.isEmitBlocked(); // only emit if there weren't parse errors var emitResult: ts.EmitResult;