From fb1ad3231c20c0223245939c488b7f5cfe5c68d8 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 14 Jan 2016 17:51:34 -0800 Subject: [PATCH 1/4] Move emit helper flags to binder. Fixes #6113 --- src/compiler/binder.ts | 88 +++++++++++++++++-- src/compiler/checker.ts | 49 +---------- src/compiler/emitter.ts | 8 +- src/compiler/types.ts | 13 +-- .../reference/asyncFunctionsAcrossFiles.js | 57 ++++++++++++ .../asyncFunctionsAcrossFiles.symbols | 32 +++++++ .../reference/asyncFunctionsAcrossFiles.types | 40 +++++++++ .../reference/classExtendsAcrossFiles.js | 71 +++++++++++++++ .../reference/classExtendsAcrossFiles.symbols | 46 ++++++++++ .../reference/classExtendsAcrossFiles.types | 52 +++++++++++ .../compiler/asyncFunctionsAcrossFiles.ts | 15 ++++ .../cases/compiler/classExtendsAcrossFiles.ts | 20 +++++ 12 files changed, 428 insertions(+), 63 deletions(-) create mode 100644 tests/baselines/reference/asyncFunctionsAcrossFiles.js create mode 100644 tests/baselines/reference/asyncFunctionsAcrossFiles.symbols create mode 100644 tests/baselines/reference/asyncFunctionsAcrossFiles.types create mode 100644 tests/baselines/reference/classExtendsAcrossFiles.js create mode 100644 tests/baselines/reference/classExtendsAcrossFiles.symbols create mode 100644 tests/baselines/reference/classExtendsAcrossFiles.types create mode 100644 tests/cases/compiler/asyncFunctionsAcrossFiles.ts create mode 100644 tests/cases/compiler/classExtendsAcrossFiles.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index f7108c5d2d714..1c1238b1479db 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -104,6 +104,7 @@ namespace ts { function createBinder(): (file: SourceFile, options: CompilerOptions) => void { let file: SourceFile; let options: CompilerOptions; + let languageVersion: ScriptTarget; let parent: Node; let container: Node; let blockScopeContainer: Node; @@ -117,6 +118,12 @@ namespace ts { let labelIndexMap: Map; let implicitLabels: number[]; + // state used for emit helpers + let hasClassExtends: boolean; + let hasAsyncFunctions: boolean; + let hasDecorators: boolean; + let hasParameterDecorators: boolean; + // If this file is an external module, then it is automatically in strict-mode according to // ES6. If it is not an external module, then we'll determine if it is in strict mode or // not depending on if we see "use strict" in certain places (or if we hit a class/namespace). @@ -129,6 +136,7 @@ namespace ts { function bindSourceFile(f: SourceFile, opts: CompilerOptions) { file = f; options = opts; + languageVersion = options.target || ScriptTarget.ES3; inStrictMode = !!file.externalModuleIndicator; classifiableNames = {}; Symbol = objectAllocator.getSymbolConstructor(); @@ -141,6 +149,7 @@ namespace ts { file = undefined; options = undefined; + languageVersion = undefined; parent = undefined; container = undefined; blockScopeContainer = undefined; @@ -150,6 +159,10 @@ namespace ts { labelStack = undefined; labelIndexMap = undefined; implicitLabels = undefined; + hasClassExtends = false; + hasAsyncFunctions = false; + hasDecorators = false; + hasParameterDecorators = false; } return bindSourceFile; @@ -423,6 +436,9 @@ namespace ts { // reset all reachability check related flags on node (for incremental scenarios) flags &= ~NodeFlags.ReachabilityCheckFlags; + // reset all emit helper flags on node (for incremental scenarios) + flags &= ~NodeFlags.EmitHelperFlags; + if (kind === SyntaxKind.InterfaceDeclaration) { seenThisKeyword = false; } @@ -453,6 +469,21 @@ namespace ts { flags = seenThisKeyword ? flags | NodeFlags.ContainsThis : flags & ~NodeFlags.ContainsThis; } + if (kind === SyntaxKind.SourceFile) { + if (hasClassExtends) { + flags |= NodeFlags.HasClassExtends; + } + if (hasDecorators) { + flags |= NodeFlags.HasDecorators; + } + if (hasParameterDecorators) { + flags |= NodeFlags.HasParamDecorators; + } + if (hasAsyncFunctions) { + flags |= NodeFlags.HasAsyncFunctions; + } + } + node.flags = flags; if (saveState) { @@ -1246,8 +1277,7 @@ namespace ts { return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Method | ((node).questionToken ? SymbolFlags.Optional : SymbolFlags.None), isObjectLiteralMethod(node) ? SymbolFlags.PropertyExcludes : SymbolFlags.MethodExcludes); case SyntaxKind.FunctionDeclaration: - checkStrictModeFunctionName(node); - return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); + return bindFunctionDeclaration(node); case SyntaxKind.Constructor: return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Constructor, /*symbolExcludes:*/ SymbolFlags.None); case SyntaxKind.GetAccessor: @@ -1263,9 +1293,7 @@ namespace ts { return bindObjectLiteralExpression(node); case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: - checkStrictModeFunctionName(node); - const bindingName = (node).name ? (node).name.text : "__function"; - return bindAnonymousDeclaration(node, SymbolFlags.Function, bindingName); + return bindFunctionExpression(node); case SyntaxKind.CallExpression: if (isInJavaScriptFile(node)) { @@ -1415,6 +1443,16 @@ namespace ts { } function bindClassLikeDeclaration(node: ClassLikeDeclaration) { + if (!isDeclarationFile(file) && !isInAmbientContext(node)) { + if (getClassExtendsHeritageClauseElement(node) !== undefined && + languageVersion < ScriptTarget.ES6) { + hasClassExtends = true; + } + if (nodeIsDecorated(node)) { + hasDecorators = true; + } + } + if (node.kind === SyntaxKind.ClassDeclaration) { bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes); } @@ -1484,6 +1522,14 @@ namespace ts { } function bindParameter(node: ParameterDeclaration) { + if (nodeIsDecorated(node) && + nodeCanBeDecorated(node) && + !isDeclarationFile(file) && + !isInAmbientContext(node)) { + hasDecorators = true; + hasParameterDecorators = true; + } + if (inStrictMode) { // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a // strict mode FunctionLikeDeclaration or FunctionExpression(13.1) @@ -1505,7 +1551,39 @@ namespace ts { } } + function bindFunctionDeclaration(node: FunctionDeclaration) { + if (!isDeclarationFile(file) && !isInAmbientContext(node)) { + if (isAsyncFunctionLike(node)) { + hasAsyncFunctions = true; + } + } + + checkStrictModeFunctionName(node); + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); + } + + function bindFunctionExpression(node: FunctionExpression) { + if (!isDeclarationFile(file) && !isInAmbientContext(node)) { + if (isAsyncFunctionLike(node)) { + hasAsyncFunctions = true; + } + } + + checkStrictModeFunctionName(node); + const bindingName = (node).name ? (node).name.text : "__function"; + return bindAnonymousDeclaration(node, SymbolFlags.Function, bindingName); + } + function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { + if (!isDeclarationFile(file) && !isInAmbientContext(node)) { + if (isAsyncFunctionLike(node)) { + hasAsyncFunctions = true; + } + if (nodeIsDecorated(node) && nodeCanBeDecorated(node)) { + hasDecorators = true; + } + } + return hasDynamicName(node) ? bindAnonymousDeclaration(node, symbolFlags, "__computed") : declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 281468d84207c..a2d1292c513a1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -174,11 +174,6 @@ namespace ts { const unionTypes: Map = {}; const intersectionTypes: Map = {}; const stringLiteralTypes: Map = {}; - let emitExtends = false; - let emitDecorate = false; - let emitParam = false; - let emitAwaiter = false; - const emitGenerator = false; const resolutionTargets: TypeSystemEntity[] = []; const resolutionResults: boolean[] = []; @@ -444,7 +439,7 @@ namespace ts { * Get symbols that represent parameter-property-declaration as parameter and as property declaration * @param parameter a parameterDeclaration node * @param parameterName a name of the parameter to get the symbols for. - * @return a tuple of two symbols + * @return a tuple of two symbols */ function getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: string): [Symbol, Symbol] { const constructoDeclaration = parameter.parent; @@ -10220,11 +10215,6 @@ namespace ts { return anyFunctionType; } - const isAsync = isAsyncFunctionLike(node); - if (isAsync) { - emitAwaiter = true; - } - const links = getNodeLinks(node); const type = getTypeOfSymbol(node.symbol); const contextSensitive = isContextSensitive(node); @@ -10273,10 +10263,6 @@ namespace ts { Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); const isAsync = isAsyncFunctionLike(node); - if (isAsync) { - emitAwaiter = true; - } - const returnOrPromisedType = node.type && (isAsync ? checkAsyncFunctionReturnType(node) : getTypeFromTypeNode(node.type)); if (!node.asteriskToken) { // return is not necessary in the body of generators @@ -12413,11 +12399,6 @@ namespace ts { } } - emitDecorate = true; - if (node.kind === SyntaxKind.Parameter) { - emitParam = true; - } - forEach(node.decorators, checkDecorator); } @@ -12435,9 +12416,6 @@ namespace ts { checkDecorators(node); checkSignatureDeclaration(node); const isAsync = isAsyncFunctionLike(node); - if (isAsync) { - emitAwaiter = true; - } // Do not use hasDynamicName here, because that returns false for well known symbols. // We want to perform checkComputedPropertyName for all computed properties, including @@ -13568,7 +13546,6 @@ namespace ts { const baseTypeNode = getClassExtendsHeritageClauseElement(node); if (baseTypeNode) { - emitExtends = emitExtends || !isInAmbientContext(node); const baseTypes = getBaseTypes(type); if (baseTypes.length && produceDiagnostics) { const baseType = baseTypes[0]; @@ -14648,10 +14625,6 @@ namespace ts { // Grammar checking checkGrammarSourceFile(node); - emitExtends = false; - emitDecorate = false; - emitParam = false; - emitAwaiter = false; potentialThisCollisions.length = 0; deferredNodes = []; @@ -14668,26 +14641,6 @@ namespace ts { potentialThisCollisions.length = 0; } - if (emitExtends) { - links.flags |= NodeCheckFlags.EmitExtends; - } - - if (emitDecorate) { - links.flags |= NodeCheckFlags.EmitDecorate; - } - - if (emitParam) { - links.flags |= NodeCheckFlags.EmitParam; - } - - if (emitAwaiter) { - links.flags |= NodeCheckFlags.EmitAwaiter; - } - - if (emitGenerator || (emitAwaiter && languageVersion < ScriptTarget.ES6)) { - links.flags |= NodeCheckFlags.EmitGenerator; - } - links.flags |= NodeCheckFlags.TypeChecked; } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index c751693dfad47..1ebbbe74ab0aa 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -7353,12 +7353,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (!compilerOptions.noEmitHelpers) { // Only Emit __extends function when target ES5. // For target ES6 and above, we can emit classDeclaration as is. - if ((languageVersion < ScriptTarget.ES6) && (!extendsEmitted && resolver.getNodeCheckFlags(node) & NodeCheckFlags.EmitExtends)) { + if ((languageVersion < ScriptTarget.ES6) && (!extendsEmitted && node.flags & NodeFlags.HasClassExtends)) { writeLines(extendsHelper); extendsEmitted = true; } - if (!decorateEmitted && resolver.getNodeCheckFlags(node) & NodeCheckFlags.EmitDecorate) { + if (!decorateEmitted && node.flags & NodeFlags.HasDecorators) { writeLines(decorateHelper); if (compilerOptions.emitDecoratorMetadata) { writeLines(metadataHelper); @@ -7366,12 +7366,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi decorateEmitted = true; } - if (!paramEmitted && resolver.getNodeCheckFlags(node) & NodeCheckFlags.EmitParam) { + if (!paramEmitted && node.flags & NodeFlags.HasParamDecorators) { writeLines(paramHelper); paramEmitted = true; } - if (!awaiterEmitted && resolver.getNodeCheckFlags(node) & NodeCheckFlags.EmitAwaiter) { + if (!awaiterEmitted && node.flags & NodeFlags.HasAsyncFunctions) { writeLines(awaiterHelper); awaiterEmitted = true; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ce4ffe05f92f2..a4632bed3b9f4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -389,11 +389,17 @@ namespace ts { ContainsThis = 1 << 18, // Interface contains references to "this" HasImplicitReturn = 1 << 19, // If function implicitly returns on one of codepaths (initialized by binding) HasExplicitReturn = 1 << 20, // If function has explicit reachable return on one of codepaths (initialized by binding) + HasClassExtends = 1 << 21, // If the file has a non-ambient class with an extends clause in ES5 or lower (initialized by binding) + HasDecorators = 1 << 22, // If the file has decorators (initialized by binding) + HasParamDecorators = 1 << 23, // If the file has parameter decorators (initialized by binding) + HasAsyncFunctions = 1 << 24, // If the file has async functions (initialized by binding) + Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async, AccessibilityModifier = Public | Private | Protected, BlockScoped = Let | Const, - ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn + ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn, + EmitHelperFlags = HasClassExtends | HasDecorators | HasParamDecorators | HasAsyncFunctions, } /* @internal */ @@ -2044,11 +2050,6 @@ namespace ts { TypeChecked = 0x00000001, // Node has been type checked LexicalThis = 0x00000002, // Lexical 'this' reference CaptureThis = 0x00000004, // Lexical 'this' used in body - EmitExtends = 0x00000008, // Emit __extends - EmitDecorate = 0x00000010, // Emit __decorate - EmitParam = 0x00000020, // Emit __param helper for decorators - EmitAwaiter = 0x00000040, // Emit __awaiter - EmitGenerator = 0x00000080, // Emit __generator SuperInstance = 0x00000100, // Instance 'super' reference SuperStatic = 0x00000200, // Static 'super' reference ContextChecked = 0x00000400, // Contextual types have been assigned diff --git a/tests/baselines/reference/asyncFunctionsAcrossFiles.js b/tests/baselines/reference/asyncFunctionsAcrossFiles.js new file mode 100644 index 0000000000000..56ff9a42b7c1b --- /dev/null +++ b/tests/baselines/reference/asyncFunctionsAcrossFiles.js @@ -0,0 +1,57 @@ +//// [tests/cases/compiler/asyncFunctionsAcrossFiles.ts] //// + +//// [a.ts] +import { b } from './b'; +export const a = { + f: async () => { + await b.f(); + } +}; +//// [b.ts] +import { a } from './a'; +export const b = { + f: async () => { + await a.f(); + } +}; + +//// [b.js] +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promise, generator) { + return new Promise(function (resolve, reject) { + generator = generator.call(thisArg, _arguments); + function cast(value) { return value instanceof Promise && value.constructor === Promise ? value : new Promise(function (resolve) { resolve(value); }); } + function onfulfill(value) { try { step("next", value); } catch (e) { reject(e); } } + function onreject(value) { try { step("throw", value); } catch (e) { reject(e); } } + function step(verb, value) { + var result = generator[verb](value); + result.done ? resolve(result.value) : cast(result.value).then(onfulfill, onreject); + } + step("next", void 0); + }); +}; +import { a } from './a'; +export const b = { + f: () => __awaiter(this, void 0, Promise, function* () { + yield a.f(); + }) +}; +//// [a.js] +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promise, generator) { + return new Promise(function (resolve, reject) { + generator = generator.call(thisArg, _arguments); + function cast(value) { return value instanceof Promise && value.constructor === Promise ? value : new Promise(function (resolve) { resolve(value); }); } + function onfulfill(value) { try { step("next", value); } catch (e) { reject(e); } } + function onreject(value) { try { step("throw", value); } catch (e) { reject(e); } } + function step(verb, value) { + var result = generator[verb](value); + result.done ? resolve(result.value) : cast(result.value).then(onfulfill, onreject); + } + step("next", void 0); + }); +}; +import { b } from './b'; +export const a = { + f: () => __awaiter(this, void 0, Promise, function* () { + yield b.f(); + }) +}; diff --git a/tests/baselines/reference/asyncFunctionsAcrossFiles.symbols b/tests/baselines/reference/asyncFunctionsAcrossFiles.symbols new file mode 100644 index 0000000000000..2e9c52d5d91d5 --- /dev/null +++ b/tests/baselines/reference/asyncFunctionsAcrossFiles.symbols @@ -0,0 +1,32 @@ +=== tests/cases/compiler/a.ts === +import { b } from './b'; +>b : Symbol(b, Decl(a.ts, 0, 8)) + +export const a = { +>a : Symbol(a, Decl(a.ts, 1, 12)) + + f: async () => { +>f : Symbol(f, Decl(a.ts, 1, 18)) + + await b.f(); +>b.f : Symbol(f, Decl(b.ts, 1, 18)) +>b : Symbol(b, Decl(a.ts, 0, 8)) +>f : Symbol(f, Decl(b.ts, 1, 18)) + } +}; +=== tests/cases/compiler/b.ts === +import { a } from './a'; +>a : Symbol(a, Decl(b.ts, 0, 8)) + +export const b = { +>b : Symbol(b, Decl(b.ts, 1, 12)) + + f: async () => { +>f : Symbol(f, Decl(b.ts, 1, 18)) + + await a.f(); +>a.f : Symbol(f, Decl(a.ts, 1, 18)) +>a : Symbol(a, Decl(b.ts, 0, 8)) +>f : Symbol(f, Decl(a.ts, 1, 18)) + } +}; diff --git a/tests/baselines/reference/asyncFunctionsAcrossFiles.types b/tests/baselines/reference/asyncFunctionsAcrossFiles.types new file mode 100644 index 0000000000000..0b94614154189 --- /dev/null +++ b/tests/baselines/reference/asyncFunctionsAcrossFiles.types @@ -0,0 +1,40 @@ +=== tests/cases/compiler/a.ts === +import { b } from './b'; +>b : { f: () => Promise; } + +export const a = { +>a : { f: () => Promise; } +>{ f: async () => { await b.f(); }} : { f: () => Promise; } + + f: async () => { +>f : () => Promise +>async () => { await b.f(); } : () => Promise + + await b.f(); +>await b.f() : void +>b.f() : Promise +>b.f : () => Promise +>b : { f: () => Promise; } +>f : () => Promise + } +}; +=== tests/cases/compiler/b.ts === +import { a } from './a'; +>a : { f: () => Promise; } + +export const b = { +>b : { f: () => Promise; } +>{ f: async () => { await a.f(); }} : { f: () => Promise; } + + f: async () => { +>f : () => Promise +>async () => { await a.f(); } : () => Promise + + await a.f(); +>await a.f() : void +>a.f() : Promise +>a.f : () => Promise +>a : { f: () => Promise; } +>f : () => Promise + } +}; diff --git a/tests/baselines/reference/classExtendsAcrossFiles.js b/tests/baselines/reference/classExtendsAcrossFiles.js new file mode 100644 index 0000000000000..b533b880036f3 --- /dev/null +++ b/tests/baselines/reference/classExtendsAcrossFiles.js @@ -0,0 +1,71 @@ +//// [tests/cases/compiler/classExtendsAcrossFiles.ts] //// + +//// [a.ts] +import { b } from './b'; +export const a = { + f: () => { + class A { } + class B extends A { } + b.f(); + } +}; +//// [b.ts] +import { a } from './a'; +export const b = { + f: () => { + class A { } + class B extends A { } + a.f(); + } +}; + +//// [b.js] +"use strict"; +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var a_1 = require('./a'); +exports.b = { + f: function () { + var A = (function () { + function A() { + } + return A; + }()); + var B = (function (_super) { + __extends(B, _super); + function B() { + _super.apply(this, arguments); + } + return B; + }(A)); + a_1.a.f(); + } +}; +//// [a.js] +"use strict"; +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var b_1 = require('./b'); +exports.a = { + f: function () { + var A = (function () { + function A() { + } + return A; + }()); + var B = (function (_super) { + __extends(B, _super); + function B() { + _super.apply(this, arguments); + } + return B; + }(A)); + b_1.b.f(); + } +}; diff --git a/tests/baselines/reference/classExtendsAcrossFiles.symbols b/tests/baselines/reference/classExtendsAcrossFiles.symbols new file mode 100644 index 0000000000000..cf81c9239122d --- /dev/null +++ b/tests/baselines/reference/classExtendsAcrossFiles.symbols @@ -0,0 +1,46 @@ +=== tests/cases/compiler/a.ts === +import { b } from './b'; +>b : Symbol(b, Decl(a.ts, 0, 8)) + +export const a = { +>a : Symbol(a, Decl(a.ts, 1, 12)) + + f: () => { +>f : Symbol(f, Decl(a.ts, 1, 18)) + + class A { } +>A : Symbol(A, Decl(a.ts, 2, 14)) + + class B extends A { } +>B : Symbol(B, Decl(a.ts, 3, 19)) +>A : Symbol(A, Decl(a.ts, 2, 14)) + + b.f(); +>b.f : Symbol(f, Decl(b.ts, 1, 18)) +>b : Symbol(b, Decl(a.ts, 0, 8)) +>f : Symbol(f, Decl(b.ts, 1, 18)) + } +}; +=== tests/cases/compiler/b.ts === +import { a } from './a'; +>a : Symbol(a, Decl(b.ts, 0, 8)) + +export const b = { +>b : Symbol(b, Decl(b.ts, 1, 12)) + + f: () => { +>f : Symbol(f, Decl(b.ts, 1, 18)) + + class A { } +>A : Symbol(A, Decl(b.ts, 2, 14)) + + class B extends A { } +>B : Symbol(B, Decl(b.ts, 3, 19)) +>A : Symbol(A, Decl(b.ts, 2, 14)) + + a.f(); +>a.f : Symbol(f, Decl(a.ts, 1, 18)) +>a : Symbol(a, Decl(b.ts, 0, 8)) +>f : Symbol(f, Decl(a.ts, 1, 18)) + } +}; diff --git a/tests/baselines/reference/classExtendsAcrossFiles.types b/tests/baselines/reference/classExtendsAcrossFiles.types new file mode 100644 index 0000000000000..fe3427ba04332 --- /dev/null +++ b/tests/baselines/reference/classExtendsAcrossFiles.types @@ -0,0 +1,52 @@ +=== tests/cases/compiler/a.ts === +import { b } from './b'; +>b : { f: () => void; } + +export const a = { +>a : { f: () => void; } +>{ f: () => { class A { } class B extends A { } b.f(); }} : { f: () => void; } + + f: () => { +>f : () => void +>() => { class A { } class B extends A { } b.f(); } : () => void + + class A { } +>A : A + + class B extends A { } +>B : B +>A : A + + b.f(); +>b.f() : void +>b.f : () => void +>b : { f: () => void; } +>f : () => void + } +}; +=== tests/cases/compiler/b.ts === +import { a } from './a'; +>a : { f: () => void; } + +export const b = { +>b : { f: () => void; } +>{ f: () => { class A { } class B extends A { } a.f(); }} : { f: () => void; } + + f: () => { +>f : () => void +>() => { class A { } class B extends A { } a.f(); } : () => void + + class A { } +>A : A + + class B extends A { } +>B : B +>A : A + + a.f(); +>a.f() : void +>a.f : () => void +>a : { f: () => void; } +>f : () => void + } +}; diff --git a/tests/cases/compiler/asyncFunctionsAcrossFiles.ts b/tests/cases/compiler/asyncFunctionsAcrossFiles.ts new file mode 100644 index 0000000000000..c5f6a220fd854 --- /dev/null +++ b/tests/cases/compiler/asyncFunctionsAcrossFiles.ts @@ -0,0 +1,15 @@ +// @target: es6 +// @filename: a.ts +import { b } from './b'; +export const a = { + f: async () => { + await b.f(); + } +}; +// @filename: b.ts +import { a } from './a'; +export const b = { + f: async () => { + await a.f(); + } +}; \ No newline at end of file diff --git a/tests/cases/compiler/classExtendsAcrossFiles.ts b/tests/cases/compiler/classExtendsAcrossFiles.ts new file mode 100644 index 0000000000000..14e227647e7d7 --- /dev/null +++ b/tests/cases/compiler/classExtendsAcrossFiles.ts @@ -0,0 +1,20 @@ +// @target: es5 +// @module: commonjs +// @filename: a.ts +import { b } from './b'; +export const a = { + f: () => { + class A { } + class B extends A { } + b.f(); + } +}; +// @filename: b.ts +import { a } from './a'; +export const b = { + f: () => { + class A { } + class B extends A { } + a.f(); + } +}; \ No newline at end of file From 1d78bafa01bd934588b8f80c75cd13b0b66754d8 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 14 Jan 2016 18:01:20 -0800 Subject: [PATCH 2/4] Removed unneeded language version check. --- src/compiler/binder.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 1c1238b1479db..3127561feaafe 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -104,7 +104,6 @@ namespace ts { function createBinder(): (file: SourceFile, options: CompilerOptions) => void { let file: SourceFile; let options: CompilerOptions; - let languageVersion: ScriptTarget; let parent: Node; let container: Node; let blockScopeContainer: Node; @@ -136,7 +135,6 @@ namespace ts { function bindSourceFile(f: SourceFile, opts: CompilerOptions) { file = f; options = opts; - languageVersion = options.target || ScriptTarget.ES3; inStrictMode = !!file.externalModuleIndicator; classifiableNames = {}; Symbol = objectAllocator.getSymbolConstructor(); @@ -149,7 +147,6 @@ namespace ts { file = undefined; options = undefined; - languageVersion = undefined; parent = undefined; container = undefined; blockScopeContainer = undefined; @@ -1444,8 +1441,7 @@ namespace ts { function bindClassLikeDeclaration(node: ClassLikeDeclaration) { if (!isDeclarationFile(file) && !isInAmbientContext(node)) { - if (getClassExtendsHeritageClauseElement(node) !== undefined && - languageVersion < ScriptTarget.ES6) { + if (getClassExtendsHeritageClauseElement(node) !== undefined) { hasClassExtends = true; } if (nodeIsDecorated(node)) { From 50ed33ea3ebaf1da2ccff980e9e899a55d293097 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 20 Jan 2016 15:43:15 -0800 Subject: [PATCH 3/4] Updated nodeIsDecorated --- src/compiler/binder.ts | 13 +++++----- src/compiler/utilities.ts | 54 ++++++++++----------------------------- 2 files changed, 20 insertions(+), 47 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5eee3c31723af..06bdf81dd43dc 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -361,8 +361,8 @@ namespace ts { // when the emitter comes back to it, it knows not to qualify the name if it was found in a containing scope. // NOTE: Nested ambient modules always should go to to 'locals' table to prevent their automatic merge - // during global merging in the checker. Why? The only case when ambient module is permitted inside another module is module augmentation - // and this case is specially handled. Module augmentations should only be merged with original module definition + // during global merging in the checker. Why? The only case when ambient module is permitted inside another module is module augmentation + // and this case is specially handled. Module augmentations should only be merged with original module definition // and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed. if (!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) { const exportKind = @@ -1527,10 +1527,9 @@ namespace ts { } function bindParameter(node: ParameterDeclaration) { - if (nodeIsDecorated(node) && - nodeCanBeDecorated(node) && - !isDeclarationFile(file) && - !isInAmbientContext(node)) { + if (!isDeclarationFile(file) && + !isInAmbientContext(node) && + nodeIsDecorated(node)) { hasDecorators = true; hasParameterDecorators = true; } @@ -1584,7 +1583,7 @@ namespace ts { if (isAsyncFunctionLike(node)) { hasAsyncFunctions = true; } - if (nodeIsDecorated(node) && nodeCanBeDecorated(node)) { + if (nodeIsDecorated(node)) { hasDecorators = true; } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 69941a6095dc4..4636c8d144cf4 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -802,8 +802,8 @@ namespace ts { } /** - * Given an super call\property node returns a closest node where either - * - super call\property is legal in the node and not legal in the parent node the node. + * Given an super call\property node returns a closest node where either + * - super call\property is legal in the node and not legal in the parent node the node. * i.e. super call is legal in constructor but not legal in the class body. * - node is arrow function (so caller might need to call getSuperContainer in case it needs to climb higher) * - super call\property is definitely illegal in the node (but might be legal in some subnode) @@ -885,54 +885,28 @@ namespace ts { // property declarations are valid if their parent is a class declaration. return node.parent.kind === SyntaxKind.ClassDeclaration; - case SyntaxKind.Parameter: - // if the parameter's parent has a body and its grandparent is a class declaration, this is a valid target; - return (node.parent).body && node.parent.parent.kind === SyntaxKind.ClassDeclaration; - case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.MethodDeclaration: // if this method has a body and its parent is a class declaration, this is a valid target. - return (node).body && node.parent.kind === SyntaxKind.ClassDeclaration; + return (node).body !== undefined + && node.parent.kind === SyntaxKind.ClassDeclaration; + + case SyntaxKind.Parameter: + // if the parameter's parent has a body and its grandparent is a class declaration, this is a valid target; + return (node.parent).body !== undefined + && (node.parent.kind === SyntaxKind.Constructor + || node.parent.kind === SyntaxKind.MethodDeclaration + || node.parent.kind === SyntaxKind.SetAccessor) + && node.parent.parent.kind === SyntaxKind.ClassDeclaration; } return false; } export function nodeIsDecorated(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.ClassDeclaration: - if (node.decorators) { - return true; - } - - return false; - - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.Parameter: - if (node.decorators) { - return true; - } - - return false; - - case SyntaxKind.GetAccessor: - if ((node).body && node.decorators) { - return true; - } - - return false; - - case SyntaxKind.MethodDeclaration: - case SyntaxKind.SetAccessor: - if ((node).body && node.decorators) { - return true; - } - - return false; - } - - return false; + return node.decorators !== undefined + && nodeCanBeDecorated(node); } export function isPropertyAccessExpression(node: Node): node is PropertyAccessExpression { From 831daead55cbb9f1a52a7690950d1a11139ec030 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 21 Jan 2016 10:01:40 -0800 Subject: [PATCH 4/4] Updated failing test --- .../reference/asyncFunctionsAcrossFiles.js | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/tests/baselines/reference/asyncFunctionsAcrossFiles.js b/tests/baselines/reference/asyncFunctionsAcrossFiles.js index 56ff9a42b7c1b..05b8ca564217f 100644 --- a/tests/baselines/reference/asyncFunctionsAcrossFiles.js +++ b/tests/baselines/reference/asyncFunctionsAcrossFiles.js @@ -16,17 +16,12 @@ export const b = { }; //// [b.js] -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promise, generator) { - return new Promise(function (resolve, reject) { - generator = generator.call(thisArg, _arguments); - function cast(value) { return value instanceof Promise && value.constructor === Promise ? value : new Promise(function (resolve) { resolve(value); }); } - function onfulfill(value) { try { step("next", value); } catch (e) { reject(e); } } - function onreject(value) { try { step("throw", value); } catch (e) { reject(e); } } - function step(verb, value) { - var result = generator[verb](value); - result.done ? resolve(result.value) : cast(result.value).then(onfulfill, onreject); - } - step("next", void 0); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new P(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.call(thisArg, _arguments)).next()); }); }; import { a } from './a'; @@ -36,17 +31,12 @@ export const b = { }) }; //// [a.js] -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promise, generator) { - return new Promise(function (resolve, reject) { - generator = generator.call(thisArg, _arguments); - function cast(value) { return value instanceof Promise && value.constructor === Promise ? value : new Promise(function (resolve) { resolve(value); }); } - function onfulfill(value) { try { step("next", value); } catch (e) { reject(e); } } - function onreject(value) { try { step("throw", value); } catch (e) { reject(e); } } - function step(verb, value) { - var result = generator[verb](value); - result.done ? resolve(result.value) : cast(result.value).then(onfulfill, onreject); - } - step("next", void 0); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new P(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.call(thisArg, _arguments)).next()); }); }; import { b } from './b';