@@ -161,7 +161,7 @@ namespace ts { |
161 | 161 | * Callers should skip the current statement and avoid any returns of '_this'. |
162 | 162 | */ |
163 | 163 | ReplaceWithReturn, |
164 | | -} |
| 164 | + } |
165 | 165 |
|
166 | 166 | export function transformES6(context: TransformationContext) { |
167 | 167 | const { |
@@ -902,15 +902,14 @@ namespace ts { |
902 | 902 |
|
903 | 903 | /** |
904 | 904 | * We want to try to avoid emitting a return statement in certain cases if a user already returned something. |
905 | | - * It would be pointless and generate dead code, so we'll try to make things a little bit prettier |
| 905 | + * It would generate obviously dead code, so we'll try to make things a little bit prettier |
906 | 906 | * by doing a minimal check on whether some common patterns always explicitly return. |
907 | 907 | */ |
908 | 908 | function isSufficientlyCoveredByReturnStatements(statement: Statement): boolean { |
909 | 909 | // A return statement is considered covered. |
910 | 910 | if (statement.kind === SyntaxKind.ReturnStatement) { |
911 | 911 | return true; |
912 | 912 | } |
913 | | - |
914 | 913 | // An if-statement with two covered branches is covered. |
915 | 914 | else if (statement.kind === SyntaxKind.IfStatement) { |
916 | 915 | const ifStatement = statement as IfStatement; |
@@ -973,6 +972,68 @@ namespace ts { |
973 | 972 | return SuperCaptureResult.NoReplacement; |
974 | 973 | } |
975 | 974 |
|
| 975 | + /** |
| 976 | + * Declares a `_this` variable for derived classes and for when arrow functions capture `this`. |
| 977 | + * |
| 978 | + * @returns The new statement offset into the `statements` array. |
| 979 | + */ |
| 980 | + function declareOrCaptureOrReturnThisForConstructorIfNeeded(statements: Statement[], ctor: ConstructorDeclaration, hasExtendsClause: boolean, statementOffset: number) { |
| 981 | + // If this isn't a derived class, just capture 'this' for arrow functions if necessary. |
| 982 | + if (!hasExtendsClause) { |
| 983 | + addCaptureThisForNodeIfNeeded(statements, ctor); |
| 984 | + return SuperCaptureResult.NoReplacement; |
| 985 | + } |
| 986 | + |
| 987 | + // Most of the time, a 'super' call will be the first real statement in a constructor body. |
| 988 | + // In these cases, we'd like to transform these into a *single* statement instead of a declaration |
| 989 | + // followed by an assignment statement for '_this'. For instance, if we emitted without an initializer, |
| 990 | + // we'd get: |
| 991 | + // |
| 992 | + // var _this; |
| 993 | + // _this = _super.call(...) || this; |
| 994 | + // |
| 995 | + // instead of |
| 996 | + // |
| 997 | + // var _this = _super.call(...) || this; |
| 998 | + // |
| 999 | + // Additionally, if the 'super()' call is the last statement, we should just avoid capturing |
| 1000 | + // entirely and immediately return the result like so: |
| 1001 | + // |
| 1002 | + // return _super.call(...) || this; |
| 1003 | + // |
| 1004 | + let firstStatement: Statement; |
| 1005 | + let superCallExpression: Expression; |
| 1006 | + |
| 1007 | + const ctorStatements = ctor.body.statements; |
| 1008 | + if (statementOffset < ctorStatements.length) { |
| 1009 | + firstStatement = ctorStatements[statementOffset]; |
| 1010 | + |
| 1011 | + if (firstStatement.kind === SyntaxKind.ExpressionStatement && isSuperCallExpression((firstStatement as ExpressionStatement).expression)) { |
| 1012 | + const superCall = (firstStatement as ExpressionStatement).expression as CallExpression; |
| 1013 | + superCallExpression = setOriginalNode( |
| 1014 | + saveStateAndInvoke(superCall, visitImmediateSuperCallInBody), |
| 1015 | + superCall |
| 1016 | + ); |
| 1017 | + } |
| 1018 | + } |
| 1019 | + |
| 1020 | + // Return the result if we have an immediate super() call on the last statement. |
| 1021 | + if (superCallExpression && statementOffset === ctorStatements.length - 1) { |
| 1022 | + statements.push(createReturn(superCallExpression)); |
| 1023 | + return SuperCaptureResult.ReplaceWithReturn; |
| 1024 | + } |
| 1025 | + |
| 1026 | + // Perform the capture. |
| 1027 | + captureThisForNode(statements, ctor, superCallExpression, firstStatement); |
| 1028 | + |
| 1029 | + // If we're actually replacing the original statement, we need to signal this to the caller. |
| 1030 | + if (superCallExpression) { |
| 1031 | + return SuperCaptureResult.ReplaceSuperCapture; |
| 1032 | + } |
| 1033 | + |
| 1034 | + return SuperCaptureResult.NoReplacement; |
| 1035 | + } |
| 1036 | + |
976 | 1037 | /** |
977 | 1038 | * Visits a parameter declaration. |
978 | 1039 | * |
@@ -1216,68 +1277,6 @@ namespace ts { |
1216 | 1277 | statements.push(forStatement); |
1217 | 1278 | } |
1218 | 1279 |
|
1219 | | - /** |
1220 | | - * Declares a `_this` variable for derived classes and for when arrow functions capture `this`. |
1221 | | - * |
1222 | | - * @returns The new statement offset into the `statements` array. |
1223 | | - */ |
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 | | - } |
1230 | | - |
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 | | - ); |
1261 | | - } |
1262 | | - } |
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; |
1276 | | - } |
1277 | | - |
1278 | | - return SuperCaptureResult.NoReplacement; |
1279 | | - } |
1280 | | - |
1281 | 1280 | /** |
1282 | 1281 | * Adds a statement to capture the `this` of a function declaration if it is needed. |
1283 | 1282 | * |
|