@@ -119,6 +119,7 @@ namespace ts {
119119 let languageVersion : ScriptTarget ;
120120 let parent : Node ;
121121 let container : Node ;
122+ let containerContainer : Node ; // Container one level up
122123 let blockScopeContainer : Node ;
123124 let inferenceContainer : Node ;
124125 let lastContainer : Node ;
@@ -187,6 +188,7 @@ namespace ts {
187188 languageVersion = undefined ;
188189 parent = undefined ;
189190 container = undefined ;
191+ containerContainer = undefined ;
190192 blockScopeContainer = undefined ;
191193 inferenceContainer = undefined ;
192194 lastContainer = undefined ;
@@ -224,13 +226,7 @@ namespace ts {
224226 symbol . flags |= symbolFlags ;
225227
226228 node . symbol = symbol ;
227-
228- if ( ! symbol . declarations ) {
229- symbol . declarations = [ node ] ;
230- }
231- else {
232- symbol . declarations . push ( node ) ;
233- }
229+ symbol . declarations = append ( symbol . declarations , node ) ;
234230
235231 if ( symbolFlags & SymbolFlags . HasExports && ! symbol . exports ) {
236232 symbol . exports = createSymbolTable ( ) ;
@@ -490,8 +486,11 @@ namespace ts {
490486 // and block-container. Then after we pop out of processing the children, we restore
491487 // these saved values.
492488 const saveContainer = container ;
489+ const saveContainerContainer = containerContainer ;
493490 const savedBlockScopeContainer = blockScopeContainer ;
494491
492+ containerContainer = container ;
493+
495494 // Depending on what kind of node this is, we may have to adjust the current container
496495 // and block-container. If the current node is a container, then it is automatically
497496 // considered the current block-container as well. Also, for containers that we know
@@ -584,7 +583,9 @@ namespace ts {
584583 else {
585584 bindChildren ( node ) ;
586585 }
586+
587587 container = saveContainer ;
588+ containerContainer = saveContainerContainer ;
588589 blockScopeContainer = savedBlockScopeContainer ;
589590 }
590591
@@ -2338,14 +2339,23 @@ namespace ts {
23382339
23392340 function bindThisPropertyAssignment ( node : BinaryExpression | PropertyAccessExpression ) {
23402341 Debug . assert ( isInJavaScriptFile ( node ) ) ;
2341- const container = getThisContainer ( node , /*includeArrowFunctions*/ false ) ;
2342- switch ( container . kind ) {
2342+ const thisContainer = getThisContainer ( node , /*includeArrowFunctions*/ false ) ;
2343+ switch ( thisContainer . kind ) {
23432344 case SyntaxKind . FunctionDeclaration :
23442345 case SyntaxKind . FunctionExpression :
2346+ let constructorSymbol = thisContainer . symbol ;
2347+ // For `f.prototype.m = function() { this.x = 0; }`, `this.x = 0` should modify `f`'s members, not the function expression.
2348+ if ( isBinaryExpression ( thisContainer . parent ) && thisContainer . parent . operatorToken . kind === SyntaxKind . EqualsToken ) {
2349+ const l = thisContainer . parent . left ;
2350+ if ( isPropertyAccessEntityNameExpression ( l ) && isPrototypeAccess ( l . expression ) ) {
2351+ constructorSymbol = getJSInitializerSymbolFromName ( l . expression . expression , containerContainer ) ;
2352+ }
2353+ }
2354+
23452355 // Declare a 'member' if the container is an ES5 class or ES6 constructor
2346- container . symbol . members = container . symbol . members || createSymbolTable ( ) ;
2356+ constructorSymbol . members = constructorSymbol . members || createSymbolTable ( ) ;
23472357 // It's acceptable for multiple 'this' assignments of the same identifier to occur
2348- declareSymbol ( container . symbol . members , container . symbol , node , SymbolFlags . Property , SymbolFlags . PropertyExcludes & ~ SymbolFlags . Property ) ;
2358+ declareSymbol ( constructorSymbol . members , constructorSymbol , node , SymbolFlags . Property , SymbolFlags . PropertyExcludes & ~ SymbolFlags . Property ) ;
23492359 break ;
23502360
23512361 case SyntaxKind . Constructor :
@@ -2355,23 +2365,26 @@ namespace ts {
23552365 case SyntaxKind . SetAccessor :
23562366 // this.foo assignment in a JavaScript class
23572367 // Bind this property to the containing class
2358- const containingClass = container . parent ;
2359- const symbolTable = hasModifier ( container , ModifierFlags . Static ) ? containingClass . symbol . exports : containingClass . symbol . members ;
2368+ const containingClass = thisContainer . parent ;
2369+ const symbolTable = hasModifier ( thisContainer , ModifierFlags . Static ) ? containingClass . symbol . exports : containingClass . symbol . members ;
23602370 declareSymbol ( symbolTable , containingClass . symbol , node , SymbolFlags . Property , SymbolFlags . None , /*isReplaceableByMethod*/ true ) ;
23612371 break ;
2372+
2373+ default :
2374+ Debug . fail ( Debug . showSyntaxKind ( thisContainer ) ) ;
23622375 }
23632376 }
23642377
23652378 function bindSpecialPropertyDeclaration ( node : PropertyAccessExpression ) {
23662379 if ( node . expression . kind === SyntaxKind . ThisKeyword ) {
23672380 bindThisPropertyAssignment ( node ) ;
23682381 }
2369- else if ( isEntityNameExpression ( node ) && node . parent . parent . kind === SyntaxKind . SourceFile ) {
2370- if ( isPropertyAccessExpression ( node . expression ) && node . expression . name . escapedText === "prototype" ) {
2371- bindPrototypePropertyAssignment ( node as PropertyAccessEntityNameExpression , node . parent ) ;
2382+ else if ( isPropertyAccessEntityNameExpression ( node ) && node . parent . parent . kind === SyntaxKind . SourceFile ) {
2383+ if ( isPrototypeAccess ( node . expression ) ) {
2384+ bindPrototypePropertyAssignment ( node , node . parent ) ;
23722385 }
23732386 else {
2374- bindStaticPropertyAssignment ( node as PropertyAccessEntityNameExpression ) ;
2387+ bindStaticPropertyAssignment ( node ) ;
23752388 }
23762389 }
23772390 }
@@ -2428,17 +2441,16 @@ namespace ts {
24282441 bindPropertyAssignment ( node . expression , node , /*isPrototypeProperty*/ false ) ;
24292442 }
24302443
2444+ function getJSInitializerSymbolFromName ( name : EntityNameExpression , lookupContainer ?: Node ) : Symbol {
2445+ return getJSInitializerSymbol ( lookupSymbolForPropertyAccess ( name , lookupContainer ) ) ;
2446+ }
2447+
24312448 function bindPropertyAssignment ( name : EntityNameExpression , propertyAccess : PropertyAccessEntityNameExpression , isPrototypeProperty : boolean ) {
2432- let symbol = getJSInitializerSymbol ( lookupSymbolForPropertyAccess ( name ) ) ;
2433- let isToplevelNamespaceableInitializer : boolean ;
2434- if ( isBinaryExpression ( propertyAccess . parent ) ) {
2435- const isPrototypeAssignment = isPropertyAccessExpression ( propertyAccess . parent . left ) && propertyAccess . parent . left . name . escapedText === "prototype" ;
2436- isToplevelNamespaceableInitializer = propertyAccess . parent . parent . parent . kind === SyntaxKind . SourceFile &&
2437- ! ! getJavascriptInitializer ( propertyAccess . parent . right , isPrototypeAssignment ) ;
2438- }
2439- else {
2440- isToplevelNamespaceableInitializer = propertyAccess . parent . parent . kind === SyntaxKind . SourceFile ;
2441- }
2449+ let symbol = getJSInitializerSymbolFromName ( name ) ;
2450+ const isToplevelNamespaceableInitializer = isBinaryExpression ( propertyAccess . parent )
2451+ ? propertyAccess . parent . parent . parent . kind === SyntaxKind . SourceFile &&
2452+ ! ! getJavascriptInitializer ( propertyAccess . parent . right , isPrototypeAccess ( propertyAccess . parent . left ) )
2453+ : propertyAccess . parent . parent . kind === SyntaxKind . SourceFile ;
24422454 if ( ! isPrototypeProperty && ( ! symbol || ! ( symbol . flags & SymbolFlags . Namespace ) ) && isToplevelNamespaceableInitializer ) {
24432455 // make symbols or add declarations for intermediate containers
24442456 const flags = SymbolFlags . Module | SymbolFlags . JSContainer ;
@@ -2469,9 +2481,9 @@ namespace ts {
24692481 declareSymbol ( symbolTable , symbol , propertyAccess , symbolFlags , symbolExcludes ) ;
24702482 }
24712483
2472- function lookupSymbolForPropertyAccess ( node : EntityNameExpression ) : Symbol | undefined {
2484+ function lookupSymbolForPropertyAccess ( node : EntityNameExpression , lookupContainer : Node = container ) : Symbol | undefined {
24732485 if ( isIdentifier ( node ) ) {
2474- return lookupSymbolForNameWorker ( container , node . escapedText ) ;
2486+ return lookupSymbolForNameWorker ( lookupContainer , node . escapedText ) ;
24752487 }
24762488 else {
24772489 const symbol = getJSInitializerSymbol ( lookupSymbolForPropertyAccess ( node . expression ) ) ;
@@ -2496,7 +2508,7 @@ namespace ts {
24962508 function bindCallExpression ( node : CallExpression ) {
24972509 // We're only inspecting call expressions to detect CommonJS modules, so we can skip
24982510 // this check if we've already seen the module indicator
2499- if ( ! file . commonJsModuleIndicator && isRequireCall ( node , /*checkArgumentIsStringLiteral */ false ) ) {
2511+ if ( ! file . commonJsModuleIndicator && isRequireCall ( node , /*checkArgumentIsStringLiteralLike */ false ) ) {
25002512 setCommonJsModuleIndicator ( node ) ;
25012513 }
25022514 }
0 commit comments