From 4aa361d4bf9f6952a9d36b3840b1e445519386be Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 16 Dec 2014 13:14:14 -0800 Subject: [PATCH] Layer the compiler so that every layer only depends on hte layers below it. The layering is now: types core scanner parser binder checker emitter program --- src/compiler/binder.ts | 4 - src/compiler/checker.ts | 22 +- src/compiler/emitter.ts | 6 +- src/compiler/parser.ts | 346 ------------------------------- src/compiler/program.ts | 374 ++++++++++++++++++++++++++++++++++ src/compiler/scanner.ts | 1 - src/compiler/tsc.ts | 11 +- src/compiler/types.ts | 9 +- src/harness/fourslash.ts | 2 +- src/harness/harness.ts | 4 +- src/harness/projectsRunner.ts | 2 +- src/services/services.ts | 11 +- 12 files changed, 390 insertions(+), 402 deletions(-) create mode 100644 src/compiler/program.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 4f8141fd866..d62a36b1bc9 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1,10 +1,6 @@ -/// -/// -/// /// module ts { - export const enum ModuleInstanceState { NonInstantiated = 0, Instantiated = 1, diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 20033d4c80b..8f133d73cda 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1,10 +1,4 @@ -/// -/// -/// -/// /// -/// -/// module ts { var nextSymbolId = 1; @@ -16,7 +10,6 @@ module ts { /// 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, fullTypeCheck: boolean): TypeChecker { - var Symbol = objectAllocator.getSymbolConstructor(); var Type = objectAllocator.getTypeConstructor(); var Signature = objectAllocator.getSignatureConstructor(); @@ -27,6 +20,7 @@ module ts { var emptySymbols: SymbolTable = {}; var compilerOptions = program.getCompilerOptions(); + var emitResolver = createResolver(); var checker: TypeChecker = { getProgram: () => program, @@ -36,9 +30,7 @@ module ts { getTypeCount: () => typeCount, isUndefinedSymbol: symbol => symbol === undefinedSymbol, isArgumentsSymbol: symbol => symbol === argumentsSymbol, - emitFiles: invokeEmitter, getDiagnostics, - getDeclarationDiagnostics, getGlobalDiagnostics, getTypeOfSymbolAtLocation, getDeclaredTypeOfSymbol, @@ -66,6 +58,7 @@ module ts { getAliasedSymbol: resolveImport, hasEarlyErrors, isEmitBlocked, + getEmitResolver: () => emitResolver, }; var undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined"); @@ -8945,12 +8938,6 @@ module ts { return getSortedDiagnostics(); } - function getDeclarationDiagnostics(targetSourceFile: SourceFile): Diagnostic[] { - var resolver = createResolver(); - checkSourceFile(targetSourceFile); - return ts.getDeclarationDiagnostics(program, resolver, targetSourceFile); - } - function getGlobalDiagnostics(): Diagnostic[] { return filter(getSortedDiagnostics(), d => !d.file); } @@ -9575,11 +9562,6 @@ module ts { }; } - function invokeEmitter(targetSourceFile?: SourceFile) { - var resolver = createResolver(); - return emitFiles(resolver, targetSourceFile); - } - function initializeTypeChecker() { // Bind all source files and propagate errors forEach(program.getSourceFiles(), file => { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 0f3e9606815..1dd0e4cd09f 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1,8 +1,4 @@ -/// -/// -/// -/// -/// +/// module ts { interface EmitTextWriter { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 582924df04b..c5337a6581d 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1,5 +1,3 @@ -/// -/// /// /// @@ -260,79 +258,6 @@ module ts { } } - // TODO (drosen, mhegazy): Move to a more appropriate file. - export function createCompilerHost(options: CompilerOptions): CompilerHost { - var currentDirectory: string; - var existingDirectories: Map = {}; - - function getCanonicalFileName(fileName: string): string { - // if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form. - // otherwise use toLowerCase as a canonical form. - return sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase(); - } - - // returned by CScript sys environment - var unsupportedFileEncodingErrorCode = -2147024809; - - function getSourceFile(filename: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile { - try { - var text = sys.readFile(filename, options.charset); - } - catch (e) { - if (onError) { - onError(e.number === unsupportedFileEncodingErrorCode ? - createCompilerDiagnostic(Diagnostics.Unsupported_file_encoding).messageText : - e.message); - } - text = ""; - } - - return text !== undefined ? createSourceFile(filename, text, languageVersion) : undefined; - } - - function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) { - function directoryExists(directoryPath: string): boolean { - if (hasProperty(existingDirectories, directoryPath)) { - return true; - } - if (sys.directoryExists(directoryPath)) { - existingDirectories[directoryPath] = true; - return true; - } - return false; - } - - function ensureDirectoriesExist(directoryPath: string) { - if (directoryPath.length > getRootLength(directoryPath) && !directoryExists(directoryPath)) { - var parentDirectory = getDirectoryPath(directoryPath); - ensureDirectoriesExist(parentDirectory); - sys.createDirectory(directoryPath); - } - } - - try { - ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName))); - sys.writeFile(fileName, data, writeByteOrderMark); - } - catch (e) { - if (onError) { - onError(e.message); - } - } - } - - return { - getSourceFile, - getDefaultLibFilename: options => combinePaths(getDirectoryPath(normalizePath(sys.getExecutingFilePath())), options.target === ScriptTarget.ES6 ? "lib.es6.d.ts" : "lib.d.ts"), - writeFile, - getCurrentDirectory: () => currentDirectory || (currentDirectory = sys.getCurrentDirectory()), - useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames, - getCanonicalFileName, - getNewLine: () => sys.newLine - }; - } - - const enum ParsingContext { SourceElements, // Elements in source file ModuleElements, // Elements in module declaration @@ -5265,275 +5190,4 @@ module ts { return grammarErrorOnFirstToken(node, Diagnostics.yield_expressions_are_not_currently_supported); } } - - export function createProgram(rootNames: string[], options: CompilerOptions, host: CompilerHost): Program { - var program: Program; - var files: SourceFile[] = []; - var filesByName: Map = {}; - var errors: Diagnostic[] = []; - var seenNoDefaultLib = options.noLib; - var commonSourceDirectory: string; - - forEach(rootNames, name => processRootFile(name, false)); - if (!seenNoDefaultLib) { - processRootFile(host.getDefaultLibFilename(options), true); - } - verifyCompilerOptions(); - errors.sort(compareDiagnostics); - program = { - getSourceFile: getSourceFile, - getSourceFiles: () => files, - getCompilerOptions: () => options, - getCompilerHost: () => host, - getDiagnostics: getDiagnostics, - getGlobalDiagnostics: getGlobalDiagnostics, - getTypeChecker: fullTypeCheckMode => createTypeChecker(program, fullTypeCheckMode), - getCommonSourceDirectory: () => commonSourceDirectory, - }; - return program; - - function getSourceFile(filename: string) { - filename = host.getCanonicalFileName(filename); - return hasProperty(filesByName, filename) ? filesByName[filename] : undefined; - } - - function getDiagnostics(sourceFile?: SourceFile): Diagnostic[] { - return sourceFile ? filter(errors, e => e.file === sourceFile) : errors; - } - - function getGlobalDiagnostics(): Diagnostic[] { - return filter(errors, e => !e.file); - } - - function hasExtension(filename: string): boolean { - return getBaseFilename(filename).indexOf(".") >= 0; - } - - function processRootFile(filename: string, isDefaultLib: boolean) { - processSourceFile(normalizePath(filename), isDefaultLib); - } - - function processSourceFile(filename: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) { - if (refEnd !== undefined && refPos !== undefined) { - var start = refPos; - var length = refEnd - refPos; - } - var diagnostic: DiagnosticMessage; - if (hasExtension(filename)) { - if (!options.allowNonTsExtensions && !fileExtensionIs(filename, ".ts")) { - diagnostic = Diagnostics.File_0_must_have_extension_ts_or_d_ts; - } - else if (!findSourceFile(filename, isDefaultLib, refFile, refPos, refEnd)) { - diagnostic = Diagnostics.File_0_not_found; - } - else if (refFile && host.getCanonicalFileName(filename) === host.getCanonicalFileName(refFile.filename)) { - diagnostic = Diagnostics.A_file_cannot_have_a_reference_to_itself; - } - } - else { - if (!(findSourceFile(filename + ".ts", isDefaultLib, refFile, refPos, refEnd) || findSourceFile(filename + ".d.ts", isDefaultLib, refFile, refPos, refEnd))) { - diagnostic = Diagnostics.File_0_not_found; - filename += ".ts"; - } - } - - if (diagnostic) { - if (refFile) { - errors.push(createFileDiagnostic(refFile, start, length, diagnostic, filename)); - } - else { - errors.push(createCompilerDiagnostic(diagnostic, filename)); - } - } - } - - // Get source file from normalized filename - function findSourceFile(filename: string, isDefaultLib: boolean, refFile?: SourceFile, refStart?: number, refLength?: number): SourceFile { - var canonicalName = host.getCanonicalFileName(filename); - if (hasProperty(filesByName, canonicalName)) { - // We've already looked for this file, use cached result - return getSourceFileFromCache(filename, canonicalName, /*useAbsolutePath*/ false); - } - else { - var normalizedAbsolutePath = getNormalizedAbsolutePath(filename, host.getCurrentDirectory()); - var canonicalAbsolutePath = host.getCanonicalFileName(normalizedAbsolutePath); - if (hasProperty(filesByName, canonicalAbsolutePath)) { - return getSourceFileFromCache(normalizedAbsolutePath, canonicalAbsolutePath, /*useAbsolutePath*/ true); - } - - // We haven't looked for this file, do so now and cache result - var file = filesByName[canonicalName] = host.getSourceFile(filename, options.target, hostErrorMessage => { - errors.push(createFileDiagnostic(refFile, refStart, refLength, - Diagnostics.Cannot_read_file_0_Colon_1, filename, hostErrorMessage)); - }); - if (file) { - seenNoDefaultLib = seenNoDefaultLib || file.hasNoDefaultLib; - - // Set the source file for normalized absolute path - filesByName[canonicalAbsolutePath] = file; - - if (!options.noResolve) { - var basePath = getDirectoryPath(filename); - processReferencedFiles(file, basePath); - processImportedModules(file, basePath); - } - if (isDefaultLib) { - files.unshift(file); - } - else { - files.push(file); - } - forEach(file.getSyntacticDiagnostics(), e => { - errors.push(e); - }); - } - } - return file; - - function getSourceFileFromCache(filename: string, canonicalName: string, useAbsolutePath: boolean): SourceFile { - var file = filesByName[canonicalName]; - if (file && host.useCaseSensitiveFileNames()) { - var sourceFileName = useAbsolutePath ? getNormalizedAbsolutePath(file.filename, host.getCurrentDirectory()) : file.filename; - if (canonicalName !== sourceFileName) { - errors.push(createFileDiagnostic(refFile, refStart, refLength, - Diagnostics.Filename_0_differs_from_already_included_filename_1_only_in_casing, filename, sourceFileName)); - } - } - return file; - } - } - - function processReferencedFiles(file: SourceFile, basePath: string) { - forEach(file.referencedFiles, ref => { - var referencedFilename = isRootedDiskPath(ref.filename) ? ref.filename : combinePaths(basePath, ref.filename); - processSourceFile(normalizePath(referencedFilename), /* isDefaultLib */ false, file, ref.pos, ref.end); - }); - } - - function processImportedModules(file: SourceFile, basePath: string) { - forEach(file.statements, node => { - if (isExternalModuleImportDeclaration(node) && - getExternalModuleImportDeclarationExpression(node).kind === SyntaxKind.StringLiteral) { - - var nameLiteral = getExternalModuleImportDeclarationExpression(node); - var moduleName = nameLiteral.text; - if (moduleName) { - var searchPath = basePath; - while (true) { - var searchName = normalizePath(combinePaths(searchPath, moduleName)); - if (findModuleSourceFile(searchName + ".ts", nameLiteral) || findModuleSourceFile(searchName + ".d.ts", nameLiteral)) { - break; - } - - var parentPath = getDirectoryPath(searchPath); - if (parentPath === searchPath) { - break; - } - searchPath = parentPath; - } - } - } - else if (node.kind === SyntaxKind.ModuleDeclaration && (node).name.kind === SyntaxKind.StringLiteral && (node.flags & NodeFlags.Ambient || isDeclarationFile(file))) { - // TypeScript 1.0 spec (April 2014): 12.1.6 - // An AmbientExternalModuleDeclaration declares an external module. - // This type of declaration is permitted only in the global module. - // The StringLiteral must specify a top - level external module name. - // Relative external module names are not permitted - forEachChild((node).body, node => { - if (isExternalModuleImportDeclaration(node) && - getExternalModuleImportDeclarationExpression(node).kind === SyntaxKind.StringLiteral) { - - var nameLiteral = getExternalModuleImportDeclarationExpression(node); - var moduleName = nameLiteral.text; - if (moduleName) { - // TypeScript 1.0 spec (April 2014): 12.1.6 - // An ExternalImportDeclaration in anAmbientExternalModuleDeclaration may reference other external modules - // only through top - level external module names. Relative external module names are not permitted. - var searchName = normalizePath(combinePaths(basePath, moduleName)); - var tsFile = findModuleSourceFile(searchName + ".ts", nameLiteral); - if (!tsFile) { - findModuleSourceFile(searchName + ".d.ts", nameLiteral); - } - } - } - }); - } - }); - - function findModuleSourceFile(filename: string, nameLiteral: LiteralExpression) { - return findSourceFile(filename, /* isDefaultLib */ false, file, nameLiteral.pos, nameLiteral.end - nameLiteral.pos); - } - } - - function verifyCompilerOptions() { - if (!options.sourceMap && (options.mapRoot || options.sourceRoot)) { - // Error to specify --mapRoot or --sourceRoot without mapSourceFiles - if (options.mapRoot) { - errors.push(createCompilerDiagnostic(Diagnostics.Option_mapRoot_cannot_be_specified_without_specifying_sourcemap_option)); - } - if (options.sourceRoot) { - errors.push(createCompilerDiagnostic(Diagnostics.Option_sourceRoot_cannot_be_specified_without_specifying_sourcemap_option)); - } - return; - } - - var firstExternalModule = forEach(files, f => isExternalModule(f) ? f : undefined); - if (firstExternalModule && options.module === ModuleKind.None) { - // We cannot use createDiagnosticFromNode because nodes do not have parents yet - var externalModuleErrorSpan = getErrorSpanForNode(firstExternalModule.externalModuleIndicator); - var errorStart = skipTrivia(firstExternalModule.text, externalModuleErrorSpan.pos); - var errorLength = externalModuleErrorSpan.end - errorStart; - errors.push(createFileDiagnostic(firstExternalModule, errorStart, errorLength, Diagnostics.Cannot_compile_external_modules_unless_the_module_flag_is_provided)); - } - - // there has to be common source directory if user specified --outdir || --sourcRoot - // if user specified --mapRoot, there needs to be common source directory if there would be multiple files being emitted - if (options.outDir || // there is --outDir specified - options.sourceRoot || // there is --sourceRoot specified - (options.mapRoot && // there is --mapRoot Specified and there would be multiple js files generated - (!options.out || firstExternalModule !== undefined))) { - - var commonPathComponents: string[]; - forEach(files, sourceFile => { - // Each file contributes into common source file path - if (!(sourceFile.flags & NodeFlags.DeclarationFile) - && !fileExtensionIs(sourceFile.filename, ".js")) { - var sourcePathComponents = getNormalizedPathComponents(sourceFile.filename, host.getCurrentDirectory()); - sourcePathComponents.pop(); // FileName is not part of directory - if (commonPathComponents) { - for (var i = 0; i < Math.min(commonPathComponents.length, sourcePathComponents.length); i++) { - if (commonPathComponents[i] !== sourcePathComponents[i]) { - if (i === 0) { - errors.push(createCompilerDiagnostic(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files)); - return; - } - - // New common path found that is 0 -> i-1 - commonPathComponents.length = i; - break; - } - } - - // If the fileComponent path completely matched and less than already found update the length - if (sourcePathComponents.length < commonPathComponents.length) { - commonPathComponents.length = sourcePathComponents.length; - } - } - else { - // first file - commonPathComponents = sourcePathComponents; - } - } - }); - - commonSourceDirectory = getNormalizedPathFromPathComponents(commonPathComponents); - if (commonSourceDirectory) { - // Make sure directory path ends with directory separator so this string can directly - // used to replace with "" to get the relative path of the source file and the relative path doesn't - // start with / making it rooted path - commonSourceDirectory += directorySeparator; - } - } - } - } } diff --git a/src/compiler/program.ts b/src/compiler/program.ts new file mode 100644 index 00000000000..7fb77dc9bdd --- /dev/null +++ b/src/compiler/program.ts @@ -0,0 +1,374 @@ +/// +/// + +module ts { + // TODO (drosen, mhegazy): Move to a more appropriate file. + export function createCompilerHost(options: CompilerOptions): CompilerHost { + var currentDirectory: string; + var existingDirectories: Map = {}; + + function getCanonicalFileName(fileName: string): string { + // if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form. + // otherwise use toLowerCase as a canonical form. + return sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase(); + } + + // returned by CScript sys environment + var unsupportedFileEncodingErrorCode = -2147024809; + + function getSourceFile(filename: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile { + try { + var text = sys.readFile(filename, options.charset); + } + catch (e) { + if (onError) { + onError(e.number === unsupportedFileEncodingErrorCode ? + createCompilerDiagnostic(Diagnostics.Unsupported_file_encoding).messageText : + e.message); + } + text = ""; + } + + return text !== undefined ? createSourceFile(filename, text, languageVersion) : undefined; + } + + function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) { + function directoryExists(directoryPath: string): boolean { + if (hasProperty(existingDirectories, directoryPath)) { + return true; + } + if (sys.directoryExists(directoryPath)) { + existingDirectories[directoryPath] = true; + return true; + } + return false; + } + + function ensureDirectoriesExist(directoryPath: string) { + if (directoryPath.length > getRootLength(directoryPath) && !directoryExists(directoryPath)) { + var parentDirectory = getDirectoryPath(directoryPath); + ensureDirectoriesExist(parentDirectory); + sys.createDirectory(directoryPath); + } + } + + try { + ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName))); + sys.writeFile(fileName, data, writeByteOrderMark); + } + catch (e) { + if (onError) { + onError(e.message); + } + } + } + + return { + getSourceFile, + getDefaultLibFilename: options => combinePaths(getDirectoryPath(normalizePath(sys.getExecutingFilePath())), options.target === ScriptTarget.ES6 ? "lib.es6.d.ts" : "lib.d.ts"), + writeFile, + getCurrentDirectory: () => currentDirectory || (currentDirectory = sys.getCurrentDirectory()), + useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames, + getCanonicalFileName, + getNewLine: () => sys.newLine + }; + } + + export function createProgram(rootNames: string[], options: CompilerOptions, host: CompilerHost): Program { + var program: Program; + var files: SourceFile[] = []; + var filesByName: Map = {}; + var errors: Diagnostic[] = []; + var seenNoDefaultLib = options.noLib; + var commonSourceDirectory: string; + + forEach(rootNames, name => processRootFile(name, false)); + if (!seenNoDefaultLib) { + processRootFile(host.getDefaultLibFilename(options), true); + } + verifyCompilerOptions(); + errors.sort(compareDiagnostics); + + var fullTypeChecker: TypeChecker; + var nonFullTypeChecker: TypeChecker; + + function getTypeChecker(full: boolean) { + if (full) { + return fullTypeChecker || (fullTypeChecker = createTypeChecker(program, full)); + } + else { + return nonFullTypeChecker || (nonFullTypeChecker = createTypeChecker(program, full)); + } + } + + program = { + getSourceFile: getSourceFile, + getSourceFiles: () => files, + getCompilerOptions: () => options, + getCompilerHost: () => host, + getDiagnostics: getDiagnostics, + getGlobalDiagnostics: getGlobalDiagnostics, + getDeclarationDiagnostics: getDeclarationDiagnostics, + getTypeChecker, + getCommonSourceDirectory: () => commonSourceDirectory, + emitFiles: invokeEmitter + }; + return program; + + function getDeclarationDiagnostics(targetSourceFile: SourceFile): Diagnostic[]{ + var fullTypeChecker = getTypeChecker(/*full:*/ true); + fullTypeChecker.getDiagnostics(targetSourceFile); + var resolver = fullTypeChecker.getEmitResolver(); + return ts.getDeclarationDiagnostics(program, resolver, targetSourceFile); + } + + function invokeEmitter(targetSourceFile?: SourceFile) { + var resolver = getTypeChecker(/*full:*/ true).getEmitResolver(); + return emitFiles(resolver, targetSourceFile); + } + + function getSourceFile(filename: string) { + filename = host.getCanonicalFileName(filename); + return hasProperty(filesByName, filename) ? filesByName[filename] : undefined; + } + + function getDiagnostics(sourceFile?: SourceFile): Diagnostic[] { + return sourceFile ? filter(errors, e => e.file === sourceFile) : errors; + } + + function getGlobalDiagnostics(): Diagnostic[] { + return filter(errors, e => !e.file); + } + + function hasExtension(filename: string): boolean { + return getBaseFilename(filename).indexOf(".") >= 0; + } + + function processRootFile(filename: string, isDefaultLib: boolean) { + processSourceFile(normalizePath(filename), isDefaultLib); + } + + function processSourceFile(filename: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) { + if (refEnd !== undefined && refPos !== undefined) { + var start = refPos; + var length = refEnd - refPos; + } + var diagnostic: DiagnosticMessage; + if (hasExtension(filename)) { + if (!options.allowNonTsExtensions && !fileExtensionIs(filename, ".ts")) { + diagnostic = Diagnostics.File_0_must_have_extension_ts_or_d_ts; + } + else if (!findSourceFile(filename, isDefaultLib, refFile, refPos, refEnd)) { + diagnostic = Diagnostics.File_0_not_found; + } + else if (refFile && host.getCanonicalFileName(filename) === host.getCanonicalFileName(refFile.filename)) { + diagnostic = Diagnostics.A_file_cannot_have_a_reference_to_itself; + } + } + else { + if (!(findSourceFile(filename + ".ts", isDefaultLib, refFile, refPos, refEnd) || findSourceFile(filename + ".d.ts", isDefaultLib, refFile, refPos, refEnd))) { + diagnostic = Diagnostics.File_0_not_found; + filename += ".ts"; + } + } + + if (diagnostic) { + if (refFile) { + errors.push(createFileDiagnostic(refFile, start, length, diagnostic, filename)); + } + else { + errors.push(createCompilerDiagnostic(diagnostic, filename)); + } + } + } + + // Get source file from normalized filename + function findSourceFile(filename: string, isDefaultLib: boolean, refFile?: SourceFile, refStart?: number, refLength?: number): SourceFile { + var canonicalName = host.getCanonicalFileName(filename); + if (hasProperty(filesByName, canonicalName)) { + // We've already looked for this file, use cached result + return getSourceFileFromCache(filename, canonicalName, /*useAbsolutePath*/ false); + } + else { + var normalizedAbsolutePath = getNormalizedAbsolutePath(filename, host.getCurrentDirectory()); + var canonicalAbsolutePath = host.getCanonicalFileName(normalizedAbsolutePath); + if (hasProperty(filesByName, canonicalAbsolutePath)) { + return getSourceFileFromCache(normalizedAbsolutePath, canonicalAbsolutePath, /*useAbsolutePath*/ true); + } + + // We haven't looked for this file, do so now and cache result + var file = filesByName[canonicalName] = host.getSourceFile(filename, options.target, hostErrorMessage => { + errors.push(createFileDiagnostic(refFile, refStart, refLength, + Diagnostics.Cannot_read_file_0_Colon_1, filename, hostErrorMessage)); + }); + if (file) { + seenNoDefaultLib = seenNoDefaultLib || file.hasNoDefaultLib; + + // Set the source file for normalized absolute path + filesByName[canonicalAbsolutePath] = file; + + if (!options.noResolve) { + var basePath = getDirectoryPath(filename); + processReferencedFiles(file, basePath); + processImportedModules(file, basePath); + } + if (isDefaultLib) { + files.unshift(file); + } + else { + files.push(file); + } + forEach(file.getSyntacticDiagnostics(), e => { + errors.push(e); + }); + } + } + return file; + + function getSourceFileFromCache(filename: string, canonicalName: string, useAbsolutePath: boolean): SourceFile { + var file = filesByName[canonicalName]; + if (file && host.useCaseSensitiveFileNames()) { + var sourceFileName = useAbsolutePath ? getNormalizedAbsolutePath(file.filename, host.getCurrentDirectory()) : file.filename; + if (canonicalName !== sourceFileName) { + errors.push(createFileDiagnostic(refFile, refStart, refLength, + Diagnostics.Filename_0_differs_from_already_included_filename_1_only_in_casing, filename, sourceFileName)); + } + } + return file; + } + } + + function processReferencedFiles(file: SourceFile, basePath: string) { + forEach(file.referencedFiles, ref => { + var referencedFilename = isRootedDiskPath(ref.filename) ? ref.filename : combinePaths(basePath, ref.filename); + processSourceFile(normalizePath(referencedFilename), /* isDefaultLib */ false, file, ref.pos, ref.end); + }); + } + + function processImportedModules(file: SourceFile, basePath: string) { + forEach(file.statements, node => { + if (isExternalModuleImportDeclaration(node) && + getExternalModuleImportDeclarationExpression(node).kind === SyntaxKind.StringLiteral) { + + var nameLiteral = getExternalModuleImportDeclarationExpression(node); + var moduleName = nameLiteral.text; + if (moduleName) { + var searchPath = basePath; + while (true) { + var searchName = normalizePath(combinePaths(searchPath, moduleName)); + if (findModuleSourceFile(searchName + ".ts", nameLiteral) || findModuleSourceFile(searchName + ".d.ts", nameLiteral)) { + break; + } + + var parentPath = getDirectoryPath(searchPath); + if (parentPath === searchPath) { + break; + } + searchPath = parentPath; + } + } + } + else if (node.kind === SyntaxKind.ModuleDeclaration && (node).name.kind === SyntaxKind.StringLiteral && (node.flags & NodeFlags.Ambient || isDeclarationFile(file))) { + // TypeScript 1.0 spec (April 2014): 12.1.6 + // An AmbientExternalModuleDeclaration declares an external module. + // This type of declaration is permitted only in the global module. + // The StringLiteral must specify a top - level external module name. + // Relative external module names are not permitted + forEachChild((node).body, node => { + if (isExternalModuleImportDeclaration(node) && + getExternalModuleImportDeclarationExpression(node).kind === SyntaxKind.StringLiteral) { + + var nameLiteral = getExternalModuleImportDeclarationExpression(node); + var moduleName = nameLiteral.text; + if (moduleName) { + // TypeScript 1.0 spec (April 2014): 12.1.6 + // An ExternalImportDeclaration in anAmbientExternalModuleDeclaration may reference other external modules + // only through top - level external module names. Relative external module names are not permitted. + var searchName = normalizePath(combinePaths(basePath, moduleName)); + var tsFile = findModuleSourceFile(searchName + ".ts", nameLiteral); + if (!tsFile) { + findModuleSourceFile(searchName + ".d.ts", nameLiteral); + } + } + } + }); + } + }); + + function findModuleSourceFile(filename: string, nameLiteral: LiteralExpression) { + return findSourceFile(filename, /* isDefaultLib */ false, file, nameLiteral.pos, nameLiteral.end - nameLiteral.pos); + } + } + + function verifyCompilerOptions() { + if (!options.sourceMap && (options.mapRoot || options.sourceRoot)) { + // Error to specify --mapRoot or --sourceRoot without mapSourceFiles + if (options.mapRoot) { + errors.push(createCompilerDiagnostic(Diagnostics.Option_mapRoot_cannot_be_specified_without_specifying_sourcemap_option)); + } + if (options.sourceRoot) { + errors.push(createCompilerDiagnostic(Diagnostics.Option_sourceRoot_cannot_be_specified_without_specifying_sourcemap_option)); + } + return; + } + + var firstExternalModule = forEach(files, f => isExternalModule(f) ? f : undefined); + if (firstExternalModule && options.module === ModuleKind.None) { + // We cannot use createDiagnosticFromNode because nodes do not have parents yet + var externalModuleErrorSpan = getErrorSpanForNode(firstExternalModule.externalModuleIndicator); + var errorStart = skipTrivia(firstExternalModule.text, externalModuleErrorSpan.pos); + var errorLength = externalModuleErrorSpan.end - errorStart; + errors.push(createFileDiagnostic(firstExternalModule, errorStart, errorLength, Diagnostics.Cannot_compile_external_modules_unless_the_module_flag_is_provided)); + } + + // there has to be common source directory if user specified --outdir || --sourcRoot + // if user specified --mapRoot, there needs to be common source directory if there would be multiple files being emitted + if (options.outDir || // there is --outDir specified + options.sourceRoot || // there is --sourceRoot specified + (options.mapRoot && // there is --mapRoot Specified and there would be multiple js files generated + (!options.out || firstExternalModule !== undefined))) { + + var commonPathComponents: string[]; + forEach(files, sourceFile => { + // Each file contributes into common source file path + if (!(sourceFile.flags & NodeFlags.DeclarationFile) + && !fileExtensionIs(sourceFile.filename, ".js")) { + var sourcePathComponents = getNormalizedPathComponents(sourceFile.filename, host.getCurrentDirectory()); + sourcePathComponents.pop(); // FileName is not part of directory + if (commonPathComponents) { + for (var i = 0; i < Math.min(commonPathComponents.length, sourcePathComponents.length); i++) { + if (commonPathComponents[i] !== sourcePathComponents[i]) { + if (i === 0) { + errors.push(createCompilerDiagnostic(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files)); + return; + } + + // New common path found that is 0 -> i-1 + commonPathComponents.length = i; + break; + } + } + + // If the fileComponent path completely matched and less than already found update the length + if (sourcePathComponents.length < commonPathComponents.length) { + commonPathComponents.length = sourcePathComponents.length; + } + } + else { + // first file + commonPathComponents = sourcePathComponents; + } + } + }); + + commonSourceDirectory = getNormalizedPathFromPathComponents(commonPathComponents); + if (commonSourceDirectory) { + // Make sure directory path ends with directory separator so this string can directly + // used to replace with "" to get the relative path of the source file and the relative path doesn't + // start with / making it rooted path + commonSourceDirectory += directorySeparator; + } + } + } + } +} \ No newline at end of file diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 9853e22d9f1..224ea22961b 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -1,4 +1,3 @@ -/// /// /// diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index bc4d48b7513..d15ad44ca1c 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -1,11 +1,4 @@ -/// -/// -/// -/// -/// -/// -/// -/// +/// /// module ts { @@ -298,7 +291,7 @@ module ts { } else { var emitStart = new Date().getTime(); - var emitOutput = checker.emitFiles(); + var emitOutput = program.emitFiles(); var emitErrors = emitOutput.diagnostics; exitStatus = emitOutput.emitResultStatus; var reportStart = new Date().getTime(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 66a2ff25eee..b164e71d0c4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1,5 +1,3 @@ -/// - module ts { export interface Map { [index: string]: T; @@ -914,10 +912,14 @@ module ts { getSourceFiles(): SourceFile[]; getCompilerOptions(): CompilerOptions; getCompilerHost(): CompilerHost; + getDiagnostics(sourceFile?: SourceFile): Diagnostic[]; getGlobalDiagnostics(): Diagnostic[]; + getDeclarationDiagnostics(sourceFile: SourceFile): Diagnostic[]; + getTypeChecker(fullTypeCheckMode: boolean): TypeChecker; getCommonSourceDirectory(): string; + emitFiles(targetSourceFile?: SourceFile): EmitResult; } export interface SourceMapSpan { @@ -959,14 +961,13 @@ module ts { export interface TypeChecker { getProgram(): Program; + getEmitResolver(): EmitResolver; getDiagnostics(sourceFile?: SourceFile): Diagnostic[]; - getDeclarationDiagnostics(sourceFile: SourceFile): Diagnostic[]; getGlobalDiagnostics(): Diagnostic[]; getNodeCount(): number; getIdentifierCount(): number; getSymbolCount(): number; getTypeCount(): number; - emitFiles(targetSourceFile?: SourceFile): EmitResult; getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type; getDeclaredTypeOfSymbol(symbol: Symbol): Type; getPropertiesOfType(type: Type): Symbol[]; diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 624b73c68a9..2dbbfe877c8 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2202,7 +2202,7 @@ module FourSlash { if (errors.length > 0) { throw new Error('Error compiling ' + fileName + ': ' + errors.map(e => e.messageText).join('\r\n')); } - checker.emitFiles(); + program.emitFiles(); result = result || ''; // Might have an empty fourslash file // Compile and execute the test diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 311e2db93ee..4e4a01ccef1 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -16,8 +16,6 @@ /// /// -/// -/// /// /// /// @@ -1053,7 +1051,7 @@ module Harness { // only emit if there weren't parse errors var emitResult: ts.EmitResult; if (!isEmitBlocked) { - emitResult = checker.emitFiles(); + emitResult = program.emitFiles(); } var errors: HarnessDiagnostic[] = []; diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index 81791c099d3..e22a412b53e 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -132,7 +132,7 @@ class ProjectRunner extends RunnerBase { if (!errors.length) { var checker = program.getTypeChecker(/*fullTypeCheck*/ true); errors = checker.getDiagnostics(); - var emitResult = checker.emitFiles(); + var emitResult = program.emitFiles(); errors = ts.concatenate(errors, emitResult.diagnostics); sourceMapData = emitResult.sourceMaps; diff --git a/src/services/services.ts b/src/services/services.ts index 4f9dcaf1051..c127947b7f9 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1,8 +1,4 @@ -/// -/// -/// -/// -/// +/// /// /// @@ -13,7 +9,6 @@ /// module ts { - export var servicesVersion = "0.4" export interface Node { @@ -2435,7 +2430,7 @@ module ts { var allDiagnostics = checker.getDiagnostics(targetSourceFile); if (compilerOptions.declaration) { // If '-d' is enabled, check for emitter error. One example of emitter error is export class implements non-export interface - allDiagnostics = allDiagnostics.concat(checker.getDeclarationDiagnostics(targetSourceFile)); + allDiagnostics = allDiagnostics.concat(program.getDeclarationDiagnostics(targetSourceFile)); } return allDiagnostics } @@ -4870,7 +4865,7 @@ module ts { // Initialize writer for CompilerHost.writeFile writer = getEmitOutputWriter; - var emitOutput = getFullTypeCheckChecker().emitFiles(sourceFile); + var emitOutput = program.emitFiles(sourceFile); // Reset writer back to undefined to make sure that we produce an error message if CompilerHost.writeFile method is called when we are not in getEmitOutput writer = undefined;