@@ -139,6 +139,30 @@ namespace ts {
139
139
loopOutParameters ?: LoopOutParameter [ ] ;
140
140
}
141
141
142
+ const enum SuperCaptureResult {
143
+ /**
144
+ * A capture may have been added for calls to 'super', but
145
+ * the caller should emit subsequent statements normally.
146
+ */
147
+ NoReplacement ,
148
+ /**
149
+ * A call to 'super()' got replaced with a capturing statement like:
150
+ *
151
+ * var _this = _super.call(...) || this;
152
+ *
153
+ * Callers should skip the current statement.
154
+ */
155
+ ReplaceSuperCapture ,
156
+ /**
157
+ * A call to 'super()' got replaced with a capturing statement like:
158
+ *
159
+ * return _super.call(...) || this;
160
+ *
161
+ * Callers should skip the current statement and avoid any returns of '_this'.
162
+ */
163
+ ReplaceWithReturn ,
164
+ }
165
+
142
166
export function transformES6 ( context : TransformationContext ) {
143
167
const {
144
168
startLexicalEnvironment,
@@ -814,6 +838,7 @@ namespace ts {
814
838
startLexicalEnvironment ( ) ;
815
839
816
840
let statementOffset = - 1 ;
841
+ let thisCaptureStatus : SuperCaptureResult | undefined ;
817
842
if ( hasSynthesizedSuper ) {
818
843
// If a super call has already been synthesized,
819
844
// we're going to assume that we should just transform everything after that.
@@ -829,7 +854,11 @@ namespace ts {
829
854
addDefaultValueAssignmentsIfNeeded ( statements , constructor ) ;
830
855
addRestParameterIfNeeded ( statements , constructor , hasSynthesizedSuper ) ;
831
856
Debug . assert ( statementOffset >= 0 , "statementOffset not initialized correctly!" ) ;
832
- statementOffset = declareOrCaptureThisForConstructorIfNeeded ( statements , constructor , ! ! extendsClauseElement , statementOffset ) ;
857
+
858
+ thisCaptureStatus = declareOrCaptureOrReturnThisForConstructorIfNeeded ( statements , constructor , ! ! extendsClauseElement , statementOffset ) ;
859
+ if ( thisCaptureStatus === SuperCaptureResult . ReplaceSuperCapture || thisCaptureStatus === SuperCaptureResult . ReplaceWithReturn ) {
860
+ statementOffset ++ ;
861
+ }
833
862
}
834
863
835
864
addDefaultSuperCallIfNeeded ( statements , constructor , extendsClauseElement , hasSynthesizedSuper ) ;
@@ -841,7 +870,9 @@ namespace ts {
841
870
842
871
// Return `_this` unless we're sure enough that it would be pointless to add a return statement.
843
872
// If there's a constructor that we can tell returns in enough places, then we *do not* want to add a return.
844
- if ( extendsClauseElement && ! ( constructor && isSufficientlyCoveredByReturnStatements ( constructor . body ) ) ) {
873
+ if ( extendsClauseElement
874
+ && thisCaptureStatus !== SuperCaptureResult . ReplaceWithReturn
875
+ && ! ( constructor && isSufficientlyCoveredByReturnStatements ( constructor . body ) ) ) {
845
876
statements . push (
846
877
createReturn (
847
878
createIdentifier ( "_this" )
@@ -1190,45 +1221,61 @@ namespace ts {
1190
1221
*
1191
1222
* @returns The new statement offset into the `statements` array.
1192
1223
*/
1193
- function declareOrCaptureThisForConstructorIfNeeded ( statements : Statement [ ] , ctor : ConstructorDeclaration , hasExtendsClause : boolean , firstNonPrologue : number ) {
1194
- if ( hasExtendsClause ) {
1195
- // Most of the time, a 'super' call will be the first real statement in a constructor body.
1196
- // In these cases, we'd like to transform these into a *single* statement instead of a declaration
1197
- // followed by an assignment statement for '_this'. For instance, if we emitted without an initializer,
1198
- // we'd get:
1199
- //
1200
- // var _this;
1201
- // _this = super();
1202
- //
1203
- // instead of
1204
- //
1205
- // var _this = super();
1206
- //
1207
- let initializer : Expression = undefined ;
1208
- let firstStatement : Statement ;
1209
- if ( firstNonPrologue < ctor . body . statements . length ) {
1210
- firstStatement = ctor . body . statements [ firstNonPrologue ] ;
1211
-
1212
- if ( firstStatement . kind === SyntaxKind . ExpressionStatement && isSuperCallExpression ( ( firstStatement as ExpressionStatement ) . expression ) ) {
1213
- const superCall = ( firstStatement as ExpressionStatement ) . expression as CallExpression ;
1214
- initializer = setOriginalNode (
1215
- saveStateAndInvoke ( superCall , visitImmediateSuperCallInBody ) ,
1216
- superCall
1217
- ) ;
1218
- }
1219
- }
1220
- captureThisForNode ( statements , ctor , /*initializer*/ initializer , firstStatement ) ;
1224
+ function declareOrCaptureOrReturnThisForConstructorIfNeeded ( statements : Statement [ ] , ctor : ConstructorDeclaration , hasExtendsClause : boolean , statementOffset : number ) {
1225
+ // If this isn't a derived class, just capture 'this' for arrow functions if necessary.
1226
+ if ( ! hasExtendsClause ) {
1227
+ addCaptureThisForNodeIfNeeded ( statements , ctor ) ;
1228
+ return SuperCaptureResult . NoReplacement ;
1229
+ }
1221
1230
1222
- // We're actually skipping an extra statement. Signal this to the caller.
1223
- if ( initializer ) {
1224
- return firstNonPrologue + 1 ;
1231
+ // Most of the time, a 'super' call will be the first real statement in a constructor body.
1232
+ // In these cases, we'd like to transform these into a *single* statement instead of a declaration
1233
+ // followed by an assignment statement for '_this'. For instance, if we emitted without an initializer,
1234
+ // we'd get:
1235
+ //
1236
+ // var _this;
1237
+ // _this = _super.call(...) || this;
1238
+ //
1239
+ // instead of
1240
+ //
1241
+ // var _this = _super.call(...) || this;
1242
+ //
1243
+ // Additionally, if the 'super()' call is the last statement, we should just avoid capturing
1244
+ // entirely and immediately return the result like so:
1245
+ //
1246
+ // return _super.call(...) || this;
1247
+ //
1248
+ let firstStatement : Statement ;
1249
+ let superCallExpression : Expression ;
1250
+
1251
+ const ctorStatements = ctor . body . statements ;
1252
+ if ( statementOffset < ctorStatements . length ) {
1253
+ firstStatement = ctorStatements [ statementOffset ] ;
1254
+
1255
+ if ( firstStatement . kind === SyntaxKind . ExpressionStatement && isSuperCallExpression ( ( firstStatement as ExpressionStatement ) . expression ) ) {
1256
+ const superCall = ( firstStatement as ExpressionStatement ) . expression as CallExpression ;
1257
+ superCallExpression = setOriginalNode (
1258
+ saveStateAndInvoke ( superCall , visitImmediateSuperCallInBody ) ,
1259
+ superCall
1260
+ ) ;
1225
1261
}
1226
1262
}
1227
- else {
1228
- addCaptureThisForNodeIfNeeded ( statements , ctor ) ;
1263
+
1264
+ // Return the result if we have an immediate super() call on the last statement.
1265
+ if ( superCallExpression && statementOffset === ctorStatements . length - 1 ) {
1266
+ statements . push ( createReturn ( superCallExpression ) ) ;
1267
+ return SuperCaptureResult . ReplaceWithReturn ;
1268
+ }
1269
+
1270
+ // Perform the capture.
1271
+ captureThisForNode ( statements , ctor , superCallExpression , firstStatement ) ;
1272
+
1273
+ // If we're actually replacing the original statement, we need to signal this to the caller.
1274
+ if ( superCallExpression ) {
1275
+ return SuperCaptureResult . ReplaceSuperCapture ;
1229
1276
}
1230
1277
1231
- return firstNonPrologue ;
1278
+ return SuperCaptureResult . NoReplacement ;
1232
1279
}
1233
1280
1234
1281
/**
0 commit comments