@@ -58,43 +58,29 @@ namespace ts {
5858 return updateParen ( node , expression ) ;
5959 }
6060
61- function visitNonOptionalPropertyAccessExpression ( node : PropertyAccessExpression , captureThisArg : boolean ) : Expression {
61+ function visitNonOptionalPropertyOrElementAccessExpression ( node : AccessExpression , captureThisArg : boolean ) : Expression {
6262 if ( isOptionalChain ( node ) ) {
6363 // If `node` is an optional chain, then it is the outermost chain of an optional expression.
6464 return visitOptionalExpression ( node , captureThisArg ) ;
6565 }
6666
67- let expression = visitNode ( node . expression , visitor , isExpression ) ;
67+ let expression : Expression = visitNode ( node . expression , visitor , isExpression ) ;
6868 Debug . assertNotNode ( expression , isSyntheticReference ) ;
6969
7070 let thisArg : Expression | undefined ;
7171 if ( captureThisArg ) {
72- // `a.b` -> { expression: `(_a = a).b`, thisArg: `_a` }
73- thisArg = createTempVariable ( hoistVariableDeclaration ) ;
74- expression = createParen ( createAssignment ( thisArg , expression ) ) ;
75- }
76-
77- expression = updatePropertyAccess ( node , expression , visitNode ( node . name , visitor , isIdentifier ) ) ;
78- return thisArg ? createSyntheticReferenceExpression ( expression , thisArg ) : expression ;
79- }
80-
81- function visitNonOptionalElementAccessExpression ( node : ElementAccessExpression , captureThisArg : boolean ) : Expression {
82- if ( isOptionalChain ( node ) ) {
83- // If `node` is an optional chain, then it is the outermost chain of an optional expression.
84- return visitOptionalExpression ( node , captureThisArg ) ;
85- }
86-
87- let expression = visitNode ( node . expression , visitor , isExpression ) ;
88- Debug . assertNotNode ( expression , isSyntheticReference ) ;
89-
90- let thisArg : Expression | undefined ;
91- if ( captureThisArg ) {
92- // `a[b]` -> { expression: `(_a = a)[b]`, thisArg: `_a` }
93- thisArg = createTempVariable ( hoistVariableDeclaration ) ;
94- expression = createParen ( createAssignment ( thisArg , expression ) ) ;
72+ if ( shouldCaptureInTempVariable ( expression ) ) {
73+ thisArg = createTempVariable ( hoistVariableDeclaration ) ;
74+ expression = createAssignment ( thisArg , expression ) ;
75+ }
76+ else {
77+ thisArg = expression ;
78+ }
9579 }
9680
97- expression = updateElementAccess ( node , expression , visitNode ( node . argumentExpression , visitor , isExpression ) ) ;
81+ expression = node . kind === SyntaxKind . PropertyAccessExpression
82+ ? updatePropertyAccess ( node , expression , visitNode ( node . name , visitor , isIdentifier ) )
83+ : updateElementAccess ( node , expression , visitNode ( node . argumentExpression , visitor , isExpression ) ) ;
9884 return thisArg ? createSyntheticReferenceExpression ( expression , thisArg ) : expression ;
9985 }
10086
@@ -109,8 +95,8 @@ namespace ts {
10995 function visitNonOptionalExpression ( node : Expression , captureThisArg : boolean ) : Expression {
11096 switch ( node . kind ) {
11197 case SyntaxKind . ParenthesizedExpression : return visitNonOptionalParenthesizedExpression ( node as ParenthesizedExpression , captureThisArg ) ;
112- case SyntaxKind . PropertyAccessExpression : return visitNonOptionalPropertyAccessExpression ( node as PropertyAccessExpression , captureThisArg ) ;
113- case SyntaxKind . ElementAccessExpression : return visitNonOptionalElementAccessExpression ( node as ElementAccessExpression , captureThisArg ) ;
98+ case SyntaxKind . PropertyAccessExpression :
99+ case SyntaxKind . ElementAccessExpression : return visitNonOptionalPropertyOrElementAccessExpression ( node as AccessExpression , captureThisArg ) ;
114100 case SyntaxKind . CallExpression : return visitNonOptionalCallExpression ( node as CallExpression , captureThisArg ) ;
115101 default : return visitNode ( node , visitor , isExpression ) ;
116102 }
@@ -119,39 +105,38 @@ namespace ts {
119105 function visitOptionalExpression ( node : OptionalChain , captureThisArg : boolean ) : Expression {
120106 const { expression, chain } = flattenChain ( node ) ;
121107 const left = visitNonOptionalExpression ( expression , isCallChain ( chain [ 0 ] ) ) ;
122- const temp = createTempVariable ( hoistVariableDeclaration ) ;
123108 const leftThisArg = isSyntheticReference ( left ) ? left . thisArg : undefined ;
124- const leftExpression = isSyntheticReference ( left ) ? left . expression : left ;
125- let rightExpression : Expression = temp ;
109+ let leftExpression = isSyntheticReference ( left ) ? left . expression : left ;
110+ let capturedLeft : Expression = leftExpression ;
111+ if ( shouldCaptureInTempVariable ( leftExpression ) ) {
112+ capturedLeft = createTempVariable ( hoistVariableDeclaration ) ;
113+ leftExpression = createAssignment ( capturedLeft , leftExpression ) ;
114+ }
115+ let rightExpression = capturedLeft ;
126116 let thisArg : Expression | undefined ;
127117 for ( let i = 0 ; i < chain . length ; i ++ ) {
128118 const segment = chain [ i ] ;
129119 switch ( segment . kind ) {
130120 case SyntaxKind . PropertyAccessExpression :
131- if ( i === chain . length - 1 && captureThisArg ) {
132- thisArg = createTempVariable ( hoistVariableDeclaration ) ;
133- rightExpression = createParen ( createAssignment ( thisArg , rightExpression ) ) ;
134- }
135- rightExpression = createPropertyAccess (
136- rightExpression ,
137- visitNode ( segment . name , visitor , isIdentifier )
138- ) ;
139- break ;
140121 case SyntaxKind . ElementAccessExpression :
141122 if ( i === chain . length - 1 && captureThisArg ) {
142- thisArg = createTempVariable ( hoistVariableDeclaration ) ;
143- rightExpression = createParen ( createAssignment ( thisArg , rightExpression ) ) ;
123+ if ( shouldCaptureInTempVariable ( rightExpression ) ) {
124+ thisArg = createTempVariable ( hoistVariableDeclaration ) ;
125+ rightExpression = createAssignment ( thisArg , rightExpression ) ;
126+ }
127+ else {
128+ thisArg = rightExpression ;
129+ }
144130 }
145- rightExpression = createElementAccess (
146- rightExpression ,
147- visitNode ( segment . argumentExpression , visitor , isExpression )
148- ) ;
131+ rightExpression = segment . kind === SyntaxKind . PropertyAccessExpression
132+ ? createPropertyAccess ( rightExpression , visitNode ( segment . name , visitor , isIdentifier ) )
133+ : createElementAccess ( rightExpression , visitNode ( segment . argumentExpression , visitor , isExpression ) ) ;
149134 break ;
150135 case SyntaxKind . CallExpression :
151136 if ( i === 0 && leftThisArg ) {
152137 rightExpression = createFunctionCall (
153138 rightExpression ,
154- leftThisArg ,
139+ leftThisArg . kind === SyntaxKind . SuperKeyword ? createThis ( ) : leftThisArg ,
155140 visitNodes ( segment . arguments , visitor , isExpression )
156141 ) ;
157142 }
@@ -168,48 +153,49 @@ namespace ts {
168153 }
169154
170155 const target = createConditional (
171- createLogicalOr (
172- createStrictEquality ( createAssignment ( temp , leftExpression ) , createNull ( ) ) ,
173- createStrictEquality ( temp , createVoidZero ( ) )
174- ) ,
156+ createNotNullCondition ( leftExpression , capturedLeft , /*invert*/ true ) ,
175157 createVoidZero ( ) ,
176- rightExpression
158+ rightExpression ,
177159 ) ;
178160 return thisArg ? createSyntheticReferenceExpression ( target , thisArg ) : target ;
179161 }
180162
181- function createNotNullCondition ( node : Expression ) {
163+ function createNotNullCondition ( left : Expression , right : Expression , invert ?: boolean ) {
182164 return createBinary (
183165 createBinary (
184- node ,
185- createToken ( SyntaxKind . ExclamationEqualsEqualsToken ) ,
166+ left ,
167+ createToken ( invert ? SyntaxKind . EqualsEqualsEqualsToken : SyntaxKind . ExclamationEqualsEqualsToken ) ,
186168 createNull ( )
187169 ) ,
188- createToken ( SyntaxKind . AmpersandAmpersandToken ) ,
170+ createToken ( invert ? SyntaxKind . BarBarToken : SyntaxKind . AmpersandAmpersandToken ) ,
189171 createBinary (
190- node ,
191- createToken ( SyntaxKind . ExclamationEqualsEqualsToken ) ,
172+ right ,
173+ createToken ( invert ? SyntaxKind . EqualsEqualsEqualsToken : SyntaxKind . ExclamationEqualsEqualsToken ) ,
192174 createVoidZero ( )
193175 )
194176 ) ;
195177 }
196178
197179 function transformNullishCoalescingExpression ( node : BinaryExpression ) {
198- const expressions : Expression [ ] = [ ] ;
199180 let left = visitNode ( node . left , visitor , isExpression ) ;
200- if ( ! isIdentifier ( left ) ) {
201- const temp = createTempVariable ( hoistVariableDeclaration ) ;
202- expressions . push ( createAssignment ( temp , left ) ) ;
203- left = temp ;
181+ let right = left ;
182+ if ( shouldCaptureInTempVariable ( left ) ) {
183+ right = createTempVariable ( hoistVariableDeclaration ) ;
184+ left = createAssignment ( right , left ) ;
204185 }
205- expressions . push (
206- createParen (
207- createConditional (
208- createNotNullCondition ( left ) ,
209- left ,
210- visitNode ( node . right , visitor , isExpression ) ) )
211- ) ;
212- return inlineExpressions ( expressions ) ;
186+ return createConditional (
187+ createNotNullCondition ( left , right ) ,
188+ right ,
189+ visitNode ( node . right , visitor , isExpression ) ,
190+ ) ;
191+ }
192+
193+ function shouldCaptureInTempVariable ( expression : Expression ) : boolean {
194+ // don't capture identifiers and `this` in a temporary variable
195+ // `super` cannot be captured as it's no real variable
196+ return ! isIdentifier ( expression ) &&
197+ expression . kind !== SyntaxKind . ThisKeyword &&
198+ expression . kind !== SyntaxKind . SuperKeyword ;
213199 }
214200 }
215201}
0 commit comments