mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 11:35:42 -06:00
Make the emitter no longer depend on the Program.
This breaks layering. Also, it means the emitter depends on too large a surface area. Now the emitter declares exactly what it needs, and only gets that.
This commit is contained in:
parent
96c3c90d9a
commit
b665323d45
@ -23,7 +23,6 @@ module ts {
|
||||
var emitResolver = createResolver();
|
||||
|
||||
var checker: TypeChecker = {
|
||||
getProgram: () => program,
|
||||
getNodeCount: () => sum(program.getSourceFiles(), "nodeCount"),
|
||||
getIdentifierCount: () => sum(program.getSourceFiles(), "identifierCount"),
|
||||
getSymbolCount: () => sum(program.getSourceFiles(), "symbolCount"),
|
||||
@ -9549,7 +9548,6 @@ module ts {
|
||||
|
||||
function createResolver(): EmitResolver {
|
||||
return {
|
||||
getProgram: () => program,
|
||||
getLocalNameOfContainer,
|
||||
getExpressionNamePrefix,
|
||||
getExportAssignmentName,
|
||||
|
||||
@ -312,17 +312,17 @@ module ts {
|
||||
};
|
||||
}
|
||||
|
||||
function getSourceFilePathInNewDir(sourceFile: SourceFile, program: Program, newDirPath: string) {
|
||||
var compilerHost = program.getCompilerHost();
|
||||
function getSourceFilePathInNewDir(sourceFile: SourceFile, host: EmitHost, newDirPath: string) {
|
||||
var compilerHost = host.getCompilerHost();
|
||||
var sourceFilePath = getNormalizedAbsolutePath(sourceFile.filename, compilerHost.getCurrentDirectory());
|
||||
sourceFilePath = sourceFilePath.replace(program.getCommonSourceDirectory(), "");
|
||||
sourceFilePath = sourceFilePath.replace(host.getCommonSourceDirectory(), "");
|
||||
return combinePaths(newDirPath, sourceFilePath);
|
||||
}
|
||||
|
||||
function getOwnEmitOutputFilePath(sourceFile: SourceFile, program: Program, extension: string){
|
||||
var compilerOptions = program.getCompilerOptions();
|
||||
function getOwnEmitOutputFilePath(sourceFile: SourceFile, host: EmitHost, extension: string){
|
||||
var compilerOptions = host.getCompilerOptions();
|
||||
if (compilerOptions.outDir) {
|
||||
var emitOutputFilePathWithoutExtension = removeFileExtension(getSourceFilePathInNewDir(sourceFile, program, compilerOptions.outDir));
|
||||
var emitOutputFilePathWithoutExtension = removeFileExtension(getSourceFilePathInNewDir(sourceFile, host, compilerOptions.outDir));
|
||||
}
|
||||
else {
|
||||
var emitOutputFilePathWithoutExtension = removeFileExtension(sourceFile.filename);
|
||||
@ -337,10 +337,10 @@ module ts {
|
||||
});
|
||||
}
|
||||
|
||||
function emitDeclarations(program: Program, resolver: EmitResolver, diagnostics: Diagnostic[], jsFilePath: string, root?: SourceFile): DeclarationEmit {
|
||||
var newLine = program.getCompilerHost().getNewLine();
|
||||
var compilerOptions = program.getCompilerOptions();
|
||||
var compilerHost = program.getCompilerHost();
|
||||
function emitDeclarations(host: EmitHost, resolver: EmitResolver, diagnostics: Diagnostic[], jsFilePath: string, root?: SourceFile): DeclarationEmit {
|
||||
var newLine = host.getCompilerHost().getNewLine();
|
||||
var compilerOptions = host.getCompilerOptions();
|
||||
var compilerHost = host.getCompilerHost();
|
||||
|
||||
var write: (s: string) => void;
|
||||
var writeLine: () => void;
|
||||
@ -1396,8 +1396,8 @@ module ts {
|
||||
var declFileName = referencedFile.flags & NodeFlags.DeclarationFile
|
||||
? referencedFile.filename // Declaration file, use declaration file name
|
||||
: shouldEmitToOwnFile(referencedFile, compilerOptions)
|
||||
? getOwnEmitOutputFilePath(referencedFile, program, ".d.ts") // Own output file so get the .d.ts file
|
||||
: removeFileExtension(compilerOptions.out) + ".d.ts";// Global out file
|
||||
? getOwnEmitOutputFilePath(referencedFile, host, ".d.ts") // Own output file so get the .d.ts file
|
||||
: removeFileExtension(compilerOptions.out) + ".d.ts";// Global out file
|
||||
|
||||
declFileName = getRelativePathToDirectoryOrUrl(
|
||||
getDirectoryPath(normalizeSlashes(jsFilePath)),
|
||||
@ -1414,7 +1414,7 @@ module ts {
|
||||
if (!compilerOptions.noResolve) {
|
||||
var addedGlobalFileReference = false;
|
||||
forEach(root.referencedFiles, fileReference => {
|
||||
var referencedFile = tryResolveScriptReference(program, root, fileReference);
|
||||
var referencedFile = tryResolveScriptReference(host, root, fileReference);
|
||||
|
||||
// All the references that are not going to be part of same file
|
||||
if (referencedFile && ((referencedFile.flags & NodeFlags.DeclarationFile) || // This is a declare file reference
|
||||
@ -1434,12 +1434,12 @@ module ts {
|
||||
else {
|
||||
// Emit references corresponding to this file
|
||||
var emittedReferencedFiles: SourceFile[] = [];
|
||||
forEach(program.getSourceFiles(), sourceFile => {
|
||||
forEach(host.getSourceFiles(), sourceFile => {
|
||||
if (!isExternalModuleOrDeclarationFile(sourceFile)) {
|
||||
// Check what references need to be added
|
||||
if (!compilerOptions.noResolve) {
|
||||
forEach(sourceFile.referencedFiles, fileReference => {
|
||||
var referencedFile = tryResolveScriptReference(program, sourceFile, fileReference);
|
||||
var referencedFile = tryResolveScriptReference(host, sourceFile, fileReference);
|
||||
|
||||
// If the reference file is a declaration file or an external module, emit that reference
|
||||
if (referencedFile && (isExternalModuleOrDeclarationFile(referencedFile) &&
|
||||
@ -1472,13 +1472,13 @@ module ts {
|
||||
}
|
||||
|
||||
// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compilerOnSave feature
|
||||
export function emitFiles(resolver: EmitResolver, targetSourceFile?: SourceFile): EmitResult {
|
||||
var program = resolver.getProgram();
|
||||
var compilerHost = program.getCompilerHost();
|
||||
var compilerOptions = program.getCompilerOptions();
|
||||
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile?: SourceFile): EmitResult {
|
||||
// var program = resolver.getProgram();
|
||||
var compilerHost = host.getCompilerHost();
|
||||
var compilerOptions = host.getCompilerOptions();
|
||||
var sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap ? [] : undefined;
|
||||
var diagnostics: Diagnostic[] = [];
|
||||
var newLine = program.getCompilerHost().getNewLine();
|
||||
var newLine = compilerHost.getNewLine();
|
||||
|
||||
function emitJavaScript(jsFilePath: string, root?: SourceFile) {
|
||||
var writer = createTextWriter(newLine);
|
||||
@ -1700,7 +1700,7 @@ module ts {
|
||||
// Add the file to tsFilePaths
|
||||
// If sourceroot option: Use the relative path corresponding to the common directory path
|
||||
// otherwise source locations relative to map file location
|
||||
var sourcesDirectoryPath = compilerOptions.sourceRoot ? program.getCommonSourceDirectory() : sourceMapDir;
|
||||
var sourcesDirectoryPath = compilerOptions.sourceRoot ? host.getCommonSourceDirectory() : sourceMapDir;
|
||||
|
||||
sourceMapData.sourceMapSources.push(getRelativePathToDirectoryOrUrl(sourcesDirectoryPath,
|
||||
node.filename,
|
||||
@ -1840,12 +1840,12 @@ module ts {
|
||||
if (root) { // emitting single module file
|
||||
// For modules or multiple emit files the mapRoot will have directory structure like the sources
|
||||
// So if src\a.ts and src\lib\b.ts are compiled together user would be moving the maps into mapRoot\a.js.map and mapRoot\lib\b.js.map
|
||||
sourceMapDir = getDirectoryPath(getSourceFilePathInNewDir(root, program, sourceMapDir));
|
||||
sourceMapDir = getDirectoryPath(getSourceFilePathInNewDir(root, host, sourceMapDir));
|
||||
}
|
||||
|
||||
if (!isRootedDiskPath(sourceMapDir) && !isUrl(sourceMapDir)) {
|
||||
// The relative paths are relative to the common directory
|
||||
sourceMapDir = combinePaths(program.getCommonSourceDirectory(), sourceMapDir);
|
||||
sourceMapDir = combinePaths(host.getCommonSourceDirectory(), sourceMapDir);
|
||||
sourceMapData.jsSourceMappingURL = getRelativePathToDirectoryOrUrl(
|
||||
getDirectoryPath(normalizePath(jsFilePath)), // get the relative sourceMapDir path based on jsFilePath
|
||||
combinePaths(sourceMapDir, sourceMapData.jsSourceMappingURL), // this is where user expects to see sourceMap
|
||||
@ -4101,7 +4101,7 @@ module ts {
|
||||
emit(root);
|
||||
}
|
||||
else {
|
||||
forEach(program.getSourceFiles(), sourceFile => {
|
||||
forEach(host.getSourceFiles(), sourceFile => {
|
||||
if (!isExternalModuleOrDeclarationFile(sourceFile)) {
|
||||
emit(sourceFile);
|
||||
}
|
||||
@ -4113,7 +4113,7 @@ module ts {
|
||||
}
|
||||
|
||||
function writeDeclarationFile(jsFilePath: string, sourceFile: SourceFile) {
|
||||
var emitDeclarationResult = emitDeclarations(program, resolver, diagnostics, jsFilePath, sourceFile);
|
||||
var emitDeclarationResult = emitDeclarations(host, resolver, diagnostics, jsFilePath, sourceFile);
|
||||
// TODO(shkamat): Should we not write any declaration file if any of them can produce error,
|
||||
// or should we just not write this file like we are doing now
|
||||
if (!emitDeclarationResult.reportedDeclarationError) {
|
||||
@ -4140,9 +4140,9 @@ module ts {
|
||||
hasSemanticErrors = resolver.hasSemanticErrors();
|
||||
isEmitBlocked = resolver.isEmitBlocked();
|
||||
|
||||
forEach(program.getSourceFiles(), sourceFile => {
|
||||
forEach(host.getSourceFiles(), sourceFile => {
|
||||
if (shouldEmitToOwnFile(sourceFile, compilerOptions)) {
|
||||
var jsFilePath = getOwnEmitOutputFilePath(sourceFile, program, ".js");
|
||||
var jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, ".js");
|
||||
emitFile(jsFilePath, sourceFile);
|
||||
}
|
||||
});
|
||||
@ -4158,13 +4158,13 @@ module ts {
|
||||
hasSemanticErrors = resolver.hasSemanticErrors(targetSourceFile);
|
||||
isEmitBlocked = resolver.isEmitBlocked(targetSourceFile);
|
||||
|
||||
var jsFilePath = getOwnEmitOutputFilePath(targetSourceFile, program, ".js");
|
||||
var jsFilePath = getOwnEmitOutputFilePath(targetSourceFile, host, ".js");
|
||||
emitFile(jsFilePath, targetSourceFile);
|
||||
}
|
||||
else if (!isDeclarationFile(targetSourceFile) && compilerOptions.out) {
|
||||
// Otherwise, if --out is specified and targetSourceFile is not a declaration file,
|
||||
// Emit all, non-external-module file, into one single output file
|
||||
forEach(program.getSourceFiles(), sourceFile => {
|
||||
forEach(host.getSourceFiles(), sourceFile => {
|
||||
if (!shouldEmitToOwnFile(sourceFile, compilerOptions)) {
|
||||
hasSemanticErrors = hasSemanticErrors || resolver.hasSemanticErrors(sourceFile);
|
||||
isEmitBlocked = isEmitBlocked || resolver.isEmitBlocked(sourceFile);
|
||||
|
||||
@ -92,15 +92,6 @@ module ts {
|
||||
var diagnosticsProducingTypeChecker: TypeChecker;
|
||||
var noDiagnosticsTypeChecker: TypeChecker;
|
||||
|
||||
function getTypeChecker(produceDiagnostics: boolean) {
|
||||
if (produceDiagnostics) {
|
||||
return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, produceDiagnostics));
|
||||
}
|
||||
else {
|
||||
return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, produceDiagnostics));
|
||||
}
|
||||
}
|
||||
|
||||
program = {
|
||||
getSourceFile: getSourceFile,
|
||||
getSourceFiles: () => files,
|
||||
@ -115,6 +106,15 @@ module ts {
|
||||
};
|
||||
return program;
|
||||
|
||||
function getTypeChecker(produceDiagnostics: boolean) {
|
||||
if (produceDiagnostics) {
|
||||
return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, produceDiagnostics));
|
||||
}
|
||||
else {
|
||||
return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, produceDiagnostics));
|
||||
}
|
||||
}
|
||||
|
||||
function getDeclarationDiagnostics(targetSourceFile: SourceFile): Diagnostic[]{
|
||||
var fullTypeChecker = getTypeChecker(/*produceDiagnostics:*/true);
|
||||
fullTypeChecker.getDiagnostics(targetSourceFile);
|
||||
@ -124,7 +124,7 @@ module ts {
|
||||
|
||||
function invokeEmitter(targetSourceFile?: SourceFile) {
|
||||
var resolver = getTypeChecker(/*produceDiagnostics:*/true).getEmitResolver();
|
||||
return emitFiles(resolver, targetSourceFile);
|
||||
return emitFiles(resolver, program, targetSourceFile);
|
||||
}
|
||||
|
||||
function getSourceFile(filename: string) {
|
||||
|
||||
@ -246,7 +246,6 @@ module ts {
|
||||
EnumMember,
|
||||
// Top-level nodes
|
||||
SourceFile,
|
||||
Program,
|
||||
|
||||
// Synthesized list
|
||||
SyntaxList,
|
||||
@ -968,7 +967,6 @@ module ts {
|
||||
}
|
||||
|
||||
export interface TypeChecker {
|
||||
getProgram(): Program;
|
||||
getEmitResolver(): EmitResolver;
|
||||
getDiagnostics(sourceFile?: SourceFile): Diagnostic[];
|
||||
getGlobalDiagnostics(): Diagnostic[];
|
||||
@ -1076,8 +1074,15 @@ module ts {
|
||||
errorModuleName?: string // If the symbol is not visible from module, module's name
|
||||
}
|
||||
|
||||
export interface EmitHost {
|
||||
getSourceFile(filename: string): SourceFile;
|
||||
getSourceFiles(): SourceFile[];
|
||||
getCompilerHost(): CompilerHost;
|
||||
getCompilerOptions(): CompilerOptions;
|
||||
getCommonSourceDirectory(): string;
|
||||
}
|
||||
|
||||
export interface EmitResolver {
|
||||
getProgram(): Program;
|
||||
getLocalNameOfContainer(container: ModuleDeclaration | EnumDeclaration): string;
|
||||
getExpressionNamePrefix(node: Identifier): string;
|
||||
getExportAssignmentName(node: SourceFile): string;
|
||||
|
||||
@ -636,11 +636,11 @@ module ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function tryResolveScriptReference(program: Program, sourceFile: SourceFile, reference: FileReference) {
|
||||
if (!program.getCompilerOptions().noResolve) {
|
||||
export function tryResolveScriptReference(host: EmitHost, sourceFile: SourceFile, reference: FileReference) {
|
||||
if (!host.getCompilerOptions().noResolve) {
|
||||
var referenceFileName = isRootedDiskPath(reference.filename) ? reference.filename : combinePaths(getDirectoryPath(sourceFile.filename), reference.filename);
|
||||
referenceFileName = getNormalizedAbsolutePath(referenceFileName, program.getCompilerHost().getCurrentDirectory());
|
||||
return program.getSourceFile(referenceFileName);
|
||||
referenceFileName = getNormalizedAbsolutePath(referenceFileName, host.getCompilerHost().getCurrentDirectory());
|
||||
return host.getSourceFile(referenceFileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -53,7 +53,7 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
var rootDir: string;
|
||||
|
||||
var result: Harness.Compiler.CompilerResult;
|
||||
var checker: ts.TypeChecker;
|
||||
var program: ts.Program;
|
||||
var options: ts.CompilerOptions;
|
||||
// equivalent to the files that will be passed on the command line
|
||||
var toBeCompiled: { unitName: string; content: string }[];
|
||||
@ -97,10 +97,10 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
});
|
||||
}
|
||||
|
||||
options = harnessCompiler.compileFiles(toBeCompiled, otherFiles, function (compileResult, _checker) {
|
||||
options = harnessCompiler.compileFiles(toBeCompiled, otherFiles, function (compileResult, _program) {
|
||||
result = compileResult;
|
||||
// The checker will be used by typeWriter
|
||||
checker = _checker;
|
||||
// The program will be used by typeWriter
|
||||
program = _program;
|
||||
}, function (settings) {
|
||||
harnessCompiler.setCompilerSettings(tcSettings);
|
||||
});
|
||||
@ -138,7 +138,7 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
lastUnit = undefined;
|
||||
rootDir = undefined;
|
||||
result = undefined;
|
||||
checker = undefined;
|
||||
program = undefined;
|
||||
options = undefined;
|
||||
toBeCompiled = undefined;
|
||||
otherFiles = undefined;
|
||||
@ -267,10 +267,10 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
// NEWTODO: Type baselines
|
||||
if (result.errors.length === 0) {
|
||||
Harness.Baseline.runBaseline('Correct expression types for ' + fileName, justName.replace(/\.ts/, '.types'), () => {
|
||||
var allFiles = toBeCompiled.concat(otherFiles).filter(file => !!checker.getProgram().getSourceFile(file.unitName));
|
||||
var allFiles = toBeCompiled.concat(otherFiles).filter(file => !!program.getSourceFile(file.unitName));
|
||||
var typeLines: string[] = [];
|
||||
var typeMap: { [fileName: string]: { [lineNum: number]: string[]; } } = {};
|
||||
var walker = new TypeWriterWalker(checker);
|
||||
var walker = new TypeWriterWalker(program);
|
||||
allFiles.forEach(file => {
|
||||
var codeLines = file.content.split('\n');
|
||||
walker.getTypes(file.unitName).forEach(result => {
|
||||
|
||||
@ -885,7 +885,7 @@ module Harness {
|
||||
|
||||
public compileFiles(inputFiles: { unitName: string; content: string }[],
|
||||
otherFiles: { unitName: string; content: string }[],
|
||||
onComplete: (result: CompilerResult, checker: ts.TypeChecker) => void,
|
||||
onComplete: (result: CompilerResult, program: ts.Program) => void,
|
||||
settingsCallback?: (settings: ts.CompilerOptions) => void,
|
||||
options?: ts.CompilerOptions) {
|
||||
|
||||
@ -1062,7 +1062,7 @@ module Harness {
|
||||
this.lastErrors = errors;
|
||||
|
||||
var result = new CompilerResult(fileOutputs, errors, program, ts.sys.getCurrentDirectory(), emitResult ? emitResult.sourceMaps : undefined);
|
||||
onComplete(result, checker);
|
||||
onComplete(result, program);
|
||||
|
||||
// reset what newline means in case the last test changed it
|
||||
ts.sys.newLine = '\r\n';
|
||||
|
||||
@ -29,7 +29,7 @@ class Test262BaselineRunner extends RunnerBase {
|
||||
filename: string;
|
||||
compilerResult: Harness.Compiler.CompilerResult;
|
||||
inputFiles: { unitName: string; content: string }[];
|
||||
checker: ts.TypeChecker;
|
||||
program: ts.Program;
|
||||
};
|
||||
|
||||
before(() => {
|
||||
@ -46,12 +46,12 @@ class Test262BaselineRunner extends RunnerBase {
|
||||
filename: testFilename,
|
||||
inputFiles: inputFiles,
|
||||
compilerResult: undefined,
|
||||
checker: undefined,
|
||||
program: undefined,
|
||||
};
|
||||
|
||||
Harness.Compiler.getCompiler().compileFiles([Test262BaselineRunner.helperFile].concat(inputFiles), /*otherFiles*/ [], (compilerResult, checker) => {
|
||||
Harness.Compiler.getCompiler().compileFiles([Test262BaselineRunner.helperFile].concat(inputFiles), /*otherFiles*/ [], (compilerResult, program) => {
|
||||
testState.compilerResult = compilerResult;
|
||||
testState.checker = checker;
|
||||
testState.program = program;
|
||||
}, /*settingsCallback*/ undefined, Test262BaselineRunner.options);
|
||||
});
|
||||
|
||||
@ -78,13 +78,13 @@ class Test262BaselineRunner extends RunnerBase {
|
||||
});
|
||||
|
||||
it('satisfies invariants', () => {
|
||||
var sourceFile = testState.checker.getProgram().getSourceFile(Test262BaselineRunner.getTestFilePath(testState.filename));
|
||||
var sourceFile = testState.program.getSourceFile(Test262BaselineRunner.getTestFilePath(testState.filename));
|
||||
Utils.assertInvariants(sourceFile, /*parent:*/ undefined);
|
||||
});
|
||||
|
||||
it('has the expected AST',() => {
|
||||
Harness.Baseline.runBaseline('has the expected AST', testState.filename + '.AST.txt',() => {
|
||||
var sourceFile = testState.checker.getProgram().getSourceFile(Test262BaselineRunner.getTestFilePath(testState.filename));
|
||||
var sourceFile = testState.program.getSourceFile(Test262BaselineRunner.getTestFilePath(testState.filename));
|
||||
return Utils.sourceFileToJSON(sourceFile);
|
||||
}, false, Test262BaselineRunner.baselineOptions);
|
||||
});
|
||||
|
||||
@ -10,11 +10,16 @@ class TypeWriterWalker {
|
||||
results: TypeWriterResult[];
|
||||
currentSourceFile: ts.SourceFile;
|
||||
|
||||
constructor(public checker: ts.TypeChecker) {
|
||||
private checker: ts.TypeChecker;
|
||||
|
||||
constructor(private program: ts.Program) {
|
||||
// Consider getting both the diagnostics checker and the non-diagnostics checker to verify
|
||||
// they are consistent.
|
||||
this.checker = program.getTypeChecker(/*produceDiagnostics:*/ true);
|
||||
}
|
||||
|
||||
public getTypes(fileName: string): TypeWriterResult[] {
|
||||
var sourceFile = this.checker.getProgram().getSourceFile(fileName);
|
||||
var sourceFile = this.program.getSourceFile(fileName);
|
||||
this.currentSourceFile = sourceFile;
|
||||
this.results = [];
|
||||
this.visitNode(sourceFile);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user