@@ -58,43 +58,29 @@ namespace ts {
58
58
return updateParen ( node , expression ) ;
59
59
}
60
60
61
- function visitNonOptionalPropertyAccessExpression ( node : PropertyAccessExpression , captureThisArg : boolean ) : Expression {
61
+ function visitNonOptionalPropertyOrElementAccessExpression ( node : AccessExpression , captureThisArg : boolean ) : Expression {
62
62
if ( isOptionalChain ( node ) ) {
63
63
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
64
64
return visitOptionalExpression ( node , captureThisArg ) ;
65
65
}
66
66
67
- let expression = visitNode ( node . expression , visitor , isExpression ) ;
67
+ let expression : Expression = visitNode ( node . expression , visitor , isExpression ) ;
68
68
Debug . assertNotNode ( expression , isSyntheticReference ) ;
69
69
70
70
let thisArg : Expression | undefined ;
71
71
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
+ }
95
79
}
96
80
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 ) ) ;
98
84
return thisArg ? createSyntheticReferenceExpression ( expression , thisArg ) : expression ;
99
85
}
100
86
@@ -109,8 +95,8 @@ namespace ts {
109
95
function visitNonOptionalExpression ( node : Expression , captureThisArg : boolean ) : Expression {
110
96
switch ( node . kind ) {
111
97
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 ) ;
114
100
case SyntaxKind . CallExpression : return visitNonOptionalCallExpression ( node as CallExpression , captureThisArg ) ;
115
101
default : return visitNode ( node , visitor , isExpression ) ;
116
102
}
@@ -119,39 +105,38 @@ namespace ts {
119
105
function visitOptionalExpression ( node : OptionalChain , captureThisArg : boolean ) : Expression {
120
106
const { expression, chain } = flattenChain ( node ) ;
121
107
const left = visitNonOptionalExpression ( expression , isCallChain ( chain [ 0 ] ) ) ;
122
- const temp = createTempVariable ( hoistVariableDeclaration ) ;
123
108
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 ;
126
116
let thisArg : Expression | undefined ;
127
117
for ( let i = 0 ; i < chain . length ; i ++ ) {
128
118
const segment = chain [ i ] ;
129
119
switch ( segment . kind ) {
130
120
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 ;
140
121
case SyntaxKind . ElementAccessExpression :
141
122
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
+ }
144
130
}
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 ) ) ;
149
134
break ;
150
135
case SyntaxKind . CallExpression :
151
136
if ( i === 0 && leftThisArg ) {
152
137
rightExpression = createFunctionCall (
153
138
rightExpression ,
154
- leftThisArg ,
139
+ leftThisArg . kind === SyntaxKind . SuperKeyword ? createThis ( ) : leftThisArg ,
155
140
visitNodes ( segment . arguments , visitor , isExpression )
156
141
) ;
157
142
}
@@ -168,48 +153,49 @@ namespace ts {
168
153
}
169
154
170
155
const target = createConditional (
171
- createLogicalOr (
172
- createStrictEquality ( createAssignment ( temp , leftExpression ) , createNull ( ) ) ,
173
- createStrictEquality ( temp , createVoidZero ( ) )
174
- ) ,
156
+ createNotNullCondition ( leftExpression , capturedLeft , /*invert*/ true ) ,
175
157
createVoidZero ( ) ,
176
- rightExpression
158
+ rightExpression ,
177
159
) ;
178
160
return thisArg ? createSyntheticReferenceExpression ( target , thisArg ) : target ;
179
161
}
180
162
181
- function createNotNullCondition ( node : Expression ) {
163
+ function createNotNullCondition ( left : Expression , right : Expression , invert ?: boolean ) {
182
164
return createBinary (
183
165
createBinary (
184
- node ,
185
- createToken ( SyntaxKind . ExclamationEqualsEqualsToken ) ,
166
+ left ,
167
+ createToken ( invert ? SyntaxKind . EqualsEqualsEqualsToken : SyntaxKind . ExclamationEqualsEqualsToken ) ,
186
168
createNull ( )
187
169
) ,
188
- createToken ( SyntaxKind . AmpersandAmpersandToken ) ,
170
+ createToken ( invert ? SyntaxKind . BarBarToken : SyntaxKind . AmpersandAmpersandToken ) ,
189
171
createBinary (
190
- node ,
191
- createToken ( SyntaxKind . ExclamationEqualsEqualsToken ) ,
172
+ right ,
173
+ createToken ( invert ? SyntaxKind . EqualsEqualsEqualsToken : SyntaxKind . ExclamationEqualsEqualsToken ) ,
192
174
createVoidZero ( )
193
175
)
194
176
) ;
195
177
}
196
178
197
179
function transformNullishCoalescingExpression ( node : BinaryExpression ) {
198
- const expressions : Expression [ ] = [ ] ;
199
180
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 ) ;
204
185
}
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 ;
213
199
}
214
200
}
215
201
}
0 commit comments