From 586404ba09f33e65e35ec06747315d9c7e6c003c Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Thu, 7 Apr 2016 15:29:44 -0700 Subject: [PATCH] record temp variable introduced in spread calls --- src/compiler/factory.ts | 71 +++++++++++++------ src/compiler/transformers/destructuring.ts | 10 ++- src/compiler/transformers/es6.ts | 14 ++-- src/compiler/transformers/es7.ts | 9 +-- src/compiler/transformers/ts.ts | 9 +-- tests/baselines/reference/newWithSpread.js | 53 +++++++------- tests/baselines/reference/newWithSpreadES5.js | 50 ++++++------- 7 files changed, 117 insertions(+), 99 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 9ce03ff77a4..670f6309955 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -149,10 +149,13 @@ namespace ts { return node; } - export function createTempVariable(location?: TextRange): Identifier { + export function createTempVariable(recordTempVariable: (node: Identifier) => void, location?: TextRange): Identifier { const name = createNode(SyntaxKind.Identifier, location); name.autoGenerateKind = GeneratedIdentifierKind.Auto; getNodeId(name); + if (recordTempVariable) { + recordTempVariable(name); + } return name; } @@ -1123,7 +1126,19 @@ namespace ts { thisArg: Expression; } - export function createCallBinding(expression: Expression, languageVersion?: ScriptTarget): CallBinding { + function shouldBeCapturedInTempVariable(node: Expression): boolean { + switch (skipParentheses(node).kind) { + case SyntaxKind.Identifier: + case SyntaxKind.ThisKeyword: + case SyntaxKind.NumericLiteral: + case SyntaxKind.StringLiteral: + return false; + default: + return true; + } + } + + export function createCallBinding(expression: Expression, recordTempVariable: (temp: Identifier) => void, languageVersion?: ScriptTarget): CallBinding { const callee = skipOuterExpressions(expression, OuterExpressionKinds.All); let thisArg: Expression; let target: LeftHandSideExpression; @@ -1138,32 +1153,44 @@ namespace ts { else { switch (callee.kind) { case SyntaxKind.PropertyAccessExpression: { - // for `a.b()` target is `(_a = a).b` and thisArg is `_a` - thisArg = createTempVariable(); - target = createPropertyAccess( - createAssignment( - thisArg, - (callee).expression, - /*location*/ (callee).expression - ), - (callee).name, + if (shouldBeCapturedInTempVariable((callee).expression)) { + // for `a.b()` target is `(_a = a).b` and thisArg is `_a` + thisArg = createTempVariable(recordTempVariable); + target = createPropertyAccess( + createAssignment( + thisArg, + (callee).expression, + /*location*/(callee).expression + ), + (callee).name, /*location*/ callee - ); + ); + } + else { + thisArg = (callee).expression; + target = callee; + } break; } case SyntaxKind.ElementAccessExpression: { - // for `a[b]()` target is `(_a = a)[b]` and thisArg is `_a` - thisArg = createTempVariable(); - target = createElementAccess( - createAssignment( - thisArg, - (callee).expression, - /*location*/ (callee).expression - ), - (callee).argumentExpression, + if (shouldBeCapturedInTempVariable((callee).expression)) { + // for `a[b]()` target is `(_a = a)[b]` and thisArg is `_a` + thisArg = createTempVariable(recordTempVariable); + target = createElementAccess( + createAssignment( + thisArg, + (callee).expression, + /*location*/(callee).expression + ), + (callee).argumentExpression, /*location*/ callee - ); + ); + } + else { + thisArg = (callee).expression; + target = callee; + } break; } diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 107ea016692..7cd37bade6c 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -66,8 +66,7 @@ namespace ts { } function emitTempVariableAssignment(value: Expression, location: TextRange) { - const name = createTempVariable(); - recordTempVariable(name); + const name = createTempVariable(recordTempVariable); emitAssignment(name, value, location); return name; } @@ -102,7 +101,7 @@ namespace ts { } function emitTempVariableAssignment(value: Expression, location: TextRange) { - const name = createTempVariable(); + const name = createTempVariable(/*recordTempVariable*/ undefined); emitAssignment(name, value, location); return name; } @@ -142,7 +141,7 @@ namespace ts { } function emitTempVariableAssignment(value: Expression, location: TextRange) { - const name = createTempVariable(); + const name = createTempVariable(/*recordTempVariable*/ undefined); emitAssignment(name, value, location, /*original*/ undefined); return name; } @@ -177,8 +176,7 @@ namespace ts { } function emitTempVariableAssignment(value: Expression, location: TextRange) { - const name = createTempVariable(); - recordTempVariable(name); + const name = createTempVariable(recordTempVariable); emitPendingAssignment(name, value, location, /*original*/ undefined); return name; } diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index d8bbdd4c326..d2de17b6243 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -1561,7 +1561,7 @@ namespace ts { const counter = createLoopVariable(); const rhsReference = expression.kind === SyntaxKind.Identifier ? createUniqueName((expression).text) - : createTempVariable(); + : createTempVariable(/*recordTempVariable*/ undefined); // Initialize LHS // var v = _a[_i]; @@ -1596,7 +1596,7 @@ namespace ts { /*modifiers*/ undefined, createVariableDeclarationList([ createVariableDeclaration( - firstDeclaration ? firstDeclaration.name : createTempVariable(), + firstDeclaration ? firstDeclaration.name : createTempVariable(/*recordTempVariable*/ undefined), createElementAccess(rhsReference, counter) ) ]), @@ -1686,8 +1686,7 @@ namespace ts { // For computed properties, we need to create a unique handle to the object // literal so we can modify it without risking internal assignments tainting the object. - const temp = createTempVariable(); - hoistVariableDeclaration(temp); + const temp = createTempVariable(hoistVariableDeclaration); // Write out the first non-computed properties, then emit the rest through indexing on the temp variable. const expressions: Expression[] = []; @@ -2232,7 +2231,7 @@ namespace ts { // We are here either because SuperKeyword was used somewhere in the expression, or // because we contain a SpreadElementExpression. - const { target, thisArg } = createCallBinding(node.expression); + const { target, thisArg } = createCallBinding(node.expression, hoistVariableDeclaration); if (node.transformFlags & TransformFlags.ContainsSpreadElementExpression) { // [source] // f(...a, b) @@ -2289,7 +2288,7 @@ namespace ts { // [output] // new ((_a = C).bind.apply(_a, [void 0].concat(a)))() - const { target, thisArg } = createCallBinding(createPropertyAccess(node.expression, "bind")); + const { target, thisArg } = createCallBinding(createPropertyAccess(node.expression, "bind"), hoistVariableDeclaration); return createNew( createFunctionApply( visitNode(target, visitor, isExpression), @@ -2380,8 +2379,7 @@ namespace ts { const tag = visitNode(node.tag, visitor, isExpression); // Allocate storage for the template site object - const temp = createTempVariable(); - hoistVariableDeclaration(temp); + const temp = createTempVariable(hoistVariableDeclaration); // Build up the template arguments and the raw and cooked strings for the template. const templateArguments: Expression[] = [temp]; diff --git a/src/compiler/transformers/es7.ts b/src/compiler/transformers/es7.ts index d04ca55db4b..5d848bc608b 100644 --- a/src/compiler/transformers/es7.ts +++ b/src/compiler/transformers/es7.ts @@ -44,11 +44,9 @@ namespace ts { let value: Expression; if (isElementAccessExpression(left)) { // Transforms `a[x] **= b` into `(_a = a)[_x = x] = Math.pow(_a[_x], b)` - const expressionTemp = createTempVariable(); - hoistVariableDeclaration(expressionTemp); + const expressionTemp = createTempVariable(hoistVariableDeclaration); - const argumentExpressionTemp = createTempVariable(); - hoistVariableDeclaration(argumentExpressionTemp); + const argumentExpressionTemp = createTempVariable(hoistVariableDeclaration); target = createElementAccess( createAssignment(expressionTemp, left.expression, /*location*/ left.expression), @@ -64,8 +62,7 @@ namespace ts { } else if (isPropertyAccessExpression(left)) { // Transforms `a.x **= b` into `(_a = a).x = Math.pow(_a.x, b)` - const expressionTemp = createTempVariable(); - hoistVariableDeclaration(expressionTemp); + const expressionTemp = createTempVariable(hoistVariableDeclaration); target = createPropertyAccess( createAssignment(expressionTemp, left.expression, /*location*/ left.expression), diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index a55c344ea62..a2fc0b56763 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -674,8 +674,7 @@ namespace ts { if (staticProperties.length > 0) { const expressions: Expression[] = []; - const temp = createTempVariable(); - hoistVariableDeclaration(temp); + const temp = createTempVariable(hoistVariableDeclaration); // To preserve the behavior of the old emitter, we explicitly indent // the body of a class with static initializers. @@ -1614,8 +1613,7 @@ namespace ts { switch (resolver.getTypeReferenceSerializationKind(typeName)) { case TypeReferenceSerializationKind.Unknown: const serialized = serializeEntityNameAsExpression(typeName, /*useFallback*/ true); - const temp = createTempVariable(); - hoistVariableDeclaration(temp); + const temp = createTempVariable(hoistVariableDeclaration); return createLogicalOr( createLogicalAnd( createStrictEquality( @@ -1701,8 +1699,7 @@ namespace ts { left = serializeEntityNameAsExpression(node.left, useFallback); } else if (useFallback) { - const temp = createTempVariable(); - hoistVariableDeclaration(temp); + const temp = createTempVariable(hoistVariableDeclaration); left = createLogicalAnd( createAssignment( temp, diff --git a/tests/baselines/reference/newWithSpread.js b/tests/baselines/reference/newWithSpread.js index 275b8698d20..91c502e50b9 100644 --- a/tests/baselines/reference/newWithSpread.js +++ b/tests/baselines/reference/newWithSpread.js @@ -129,52 +129,53 @@ var h; var i; // Basic expression new f(1, 2, "string"); -new ((_a = f).bind.apply(_a, [void 0, 1, 2].concat(a)))(); -new ((_b = f).bind.apply(_b, [void 0, 1, 2].concat(a, ["string"])))(); +new (f.bind.apply(f, [void 0, 1, 2].concat(a)))(); +new (f.bind.apply(f, [void 0, 1, 2].concat(a, ["string"])))(); // Multiple spreads arguments -new ((_c = f2).bind.apply(_c, [void 0].concat(a, a)))(); -new ((_d = f).bind.apply(_d, [void 0, 1, 2].concat(a, a)))(); +new (f2.bind.apply(f2, [void 0].concat(a, a)))(); +new (f.bind.apply(f, [void 0, 1, 2].concat(a, a)))(); // Call expression new f(1, 2, "string")(); -new ((_e = f).bind.apply(_e, [void 0, 1, 2].concat(a)))()(); -new ((_f = f).bind.apply(_f, [void 0, 1, 2].concat(a, ["string"])))()(); +new (f.bind.apply(f, [void 0, 1, 2].concat(a)))()(); +new (f.bind.apply(f, [void 0, 1, 2].concat(a, ["string"])))()(); // Property access expression new b.f(1, 2, "string"); -new ((_g = b.f).bind.apply(_g, [void 0, 1, 2].concat(a)))(); -new ((_h = b.f).bind.apply(_h, [void 0, 1, 2].concat(a, ["string"])))(); +new ((_a = b.f).bind.apply(_a, [void 0, 1, 2].concat(a)))(); +new ((_b = b.f).bind.apply(_b, [void 0, 1, 2].concat(a, ["string"])))(); // Parenthesised expression new (b.f)(1, 2, "string"); -new ((_j = (b.f)).bind.apply(_j, [void 0, 1, 2].concat(a)))(); -new ((_k = (b.f)).bind.apply(_k, [void 0, 1, 2].concat(a, ["string"])))(); +new ((_c = (b.f)).bind.apply(_c, [void 0, 1, 2].concat(a)))(); +new ((_d = (b.f)).bind.apply(_d, [void 0, 1, 2].concat(a, ["string"])))(); // Element access expression new d[1].f(1, 2, "string"); -new ((_l = d[1].f).bind.apply(_l, [void 0, 1, 2].concat(a)))(); -new ((_m = d[1].f).bind.apply(_m, [void 0, 1, 2].concat(a, ["string"])))(); +new ((_e = d[1].f).bind.apply(_e, [void 0, 1, 2].concat(a)))(); +new ((_f = d[1].f).bind.apply(_f, [void 0, 1, 2].concat(a, ["string"])))(); // Element access expression with a punctuated key new e["a-b"].f(1, 2, "string"); -new ((_o = e["a-b"].f).bind.apply(_o, [void 0, 1, 2].concat(a)))(); -new ((_p = e["a-b"].f).bind.apply(_p, [void 0, 1, 2].concat(a, ["string"])))(); +new ((_g = e["a-b"].f).bind.apply(_g, [void 0, 1, 2].concat(a)))(); +new ((_h = e["a-b"].f).bind.apply(_h, [void 0, 1, 2].concat(a, ["string"])))(); // Basic expression new B(1, 2, "string"); -new ((_q = B).bind.apply(_q, [void 0, 1, 2].concat(a)))(); -new ((_r = B).bind.apply(_r, [void 0, 1, 2].concat(a, ["string"])))(); +new (B.bind.apply(B, [void 0, 1, 2].concat(a)))(); +new (B.bind.apply(B, [void 0, 1, 2].concat(a, ["string"])))(); // Property access expression new c["a-b"](1, 2, "string"); -new ((_s = c["a-b"]).bind.apply(_s, [void 0, 1, 2].concat(a)))(); -new ((_t = c["a-b"]).bind.apply(_t, [void 0, 1, 2].concat(a, ["string"])))(); +new ((_j = c["a-b"]).bind.apply(_j, [void 0, 1, 2].concat(a)))(); +new ((_k = c["a-b"]).bind.apply(_k, [void 0, 1, 2].concat(a, ["string"])))(); // Parenthesised expression new (c["a-b"])(1, 2, "string"); -new ((_u = (c["a-b"])).bind.apply(_u, [void 0, 1, 2].concat(a)))(); -new ((_v = (c["a-b"])).bind.apply(_v, [void 0, 1, 2].concat(a, ["string"])))(); +new ((_l = (c["a-b"])).bind.apply(_l, [void 0, 1, 2].concat(a)))(); +new ((_m = (c["a-b"])).bind.apply(_m, [void 0, 1, 2].concat(a, ["string"])))(); // Element access expression new g[1]["a-b"](1, 2, "string"); -new ((_w = g[1]["a-b"]).bind.apply(_w, [void 0, 1, 2].concat(a)))(); -new ((_x = g[1]["a-b"]).bind.apply(_x, [void 0, 1, 2].concat(a, ["string"])))(); +new ((_o = g[1]["a-b"]).bind.apply(_o, [void 0, 1, 2].concat(a)))(); +new ((_p = g[1]["a-b"]).bind.apply(_p, [void 0, 1, 2].concat(a, ["string"])))(); // Element access expression with a punctuated key new h["a-b"]["a-b"](1, 2, "string"); -new ((_y = h["a-b"]["a-b"]).bind.apply(_y, [void 0, 1, 2].concat(a)))(); -new ((_z = h["a-b"]["a-b"]).bind.apply(_z, [void 0, 1, 2].concat(a, ["string"])))(); +new ((_q = h["a-b"]["a-b"]).bind.apply(_q, [void 0, 1, 2].concat(a)))(); +new ((_r = h["a-b"]["a-b"]).bind.apply(_r, [void 0, 1, 2].concat(a, ["string"])))(); // Element access expression with a number new i["a-b"][1](1, 2, "string"); -new ((_0 = i["a-b"][1]).bind.apply(_0, [void 0, 1, 2].concat(a)))(); -new ((_1 = i["a-b"][1]).bind.apply(_1, [void 0, 1, 2].concat(a, ["string"])))(); +new ((_s = i["a-b"][1]).bind.apply(_s, [void 0, 1, 2].concat(a)))(); +new ((_t = i["a-b"][1]).bind.apply(_t, [void 0, 1, 2].concat(a, ["string"])))(); +var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t; diff --git a/tests/baselines/reference/newWithSpreadES5.js b/tests/baselines/reference/newWithSpreadES5.js index fffa53d7c90..92904062d0a 100644 --- a/tests/baselines/reference/newWithSpreadES5.js +++ b/tests/baselines/reference/newWithSpreadES5.js @@ -128,53 +128,53 @@ var h; var i; // Basic expression new f(1, 2, "string"); -new (f.bind.apply(f, [void 0].concat([1, 2], a)))(); -new (f.bind.apply(f, [void 0].concat([1, 2], a, ["string"])))(); +new (f.bind.apply(f, [void 0, 1, 2].concat(a)))(); +new (f.bind.apply(f, [void 0, 1, 2].concat(a, ["string"])))(); // Multiple spreads arguments new (f2.bind.apply(f2, [void 0].concat(a, a)))(); -new (f.bind.apply(f, [void 0].concat([1, 2], a, a)))(); +new (f.bind.apply(f, [void 0, 1, 2].concat(a, a)))(); // Call expression new f(1, 2, "string")(); -new (f.bind.apply(f, [void 0].concat([1, 2], a)))()(); -new (f.bind.apply(f, [void 0].concat([1, 2], a, ["string"])))()(); +new (f.bind.apply(f, [void 0, 1, 2].concat(a)))()(); +new (f.bind.apply(f, [void 0, 1, 2].concat(a, ["string"])))()(); // Property access expression new b.f(1, 2, "string"); -new ((_a = b.f).bind.apply(_a, [void 0].concat([1, 2], a)))(); -new ((_b = b.f).bind.apply(_b, [void 0].concat([1, 2], a, ["string"])))(); +new ((_a = b.f).bind.apply(_a, [void 0, 1, 2].concat(a)))(); +new ((_b = b.f).bind.apply(_b, [void 0, 1, 2].concat(a, ["string"])))(); // Parenthesised expression new (b.f)(1, 2, "string"); -new ((_c = (b.f)).bind.apply(_c, [void 0].concat([1, 2], a)))(); -new ((_d = (b.f)).bind.apply(_d, [void 0].concat([1, 2], a, ["string"])))(); +new ((_c = (b.f)).bind.apply(_c, [void 0, 1, 2].concat(a)))(); +new ((_d = (b.f)).bind.apply(_d, [void 0, 1, 2].concat(a, ["string"])))(); // Element access expression new d[1].f(1, 2, "string"); -new ((_e = d[1].f).bind.apply(_e, [void 0].concat([1, 2], a)))(); -new ((_f = d[1].f).bind.apply(_f, [void 0].concat([1, 2], a, ["string"])))(); +new ((_e = d[1].f).bind.apply(_e, [void 0, 1, 2].concat(a)))(); +new ((_f = d[1].f).bind.apply(_f, [void 0, 1, 2].concat(a, ["string"])))(); // Element access expression with a punctuated key new e["a-b"].f(1, 2, "string"); -new ((_g = e["a-b"].f).bind.apply(_g, [void 0].concat([1, 2], a)))(); -new ((_h = e["a-b"].f).bind.apply(_h, [void 0].concat([1, 2], a, ["string"])))(); +new ((_g = e["a-b"].f).bind.apply(_g, [void 0, 1, 2].concat(a)))(); +new ((_h = e["a-b"].f).bind.apply(_h, [void 0, 1, 2].concat(a, ["string"])))(); // Basic expression new B(1, 2, "string"); -new (B.bind.apply(B, [void 0].concat([1, 2], a)))(); -new (B.bind.apply(B, [void 0].concat([1, 2], a, ["string"])))(); +new (B.bind.apply(B, [void 0, 1, 2].concat(a)))(); +new (B.bind.apply(B, [void 0, 1, 2].concat(a, ["string"])))(); // Property access expression new c["a-b"](1, 2, "string"); -new ((_j = c["a-b"]).bind.apply(_j, [void 0].concat([1, 2], a)))(); -new ((_k = c["a-b"]).bind.apply(_k, [void 0].concat([1, 2], a, ["string"])))(); +new ((_j = c["a-b"]).bind.apply(_j, [void 0, 1, 2].concat(a)))(); +new ((_k = c["a-b"]).bind.apply(_k, [void 0, 1, 2].concat(a, ["string"])))(); // Parenthesised expression new (c["a-b"])(1, 2, "string"); -new ((_l = (c["a-b"])).bind.apply(_l, [void 0].concat([1, 2], a)))(); -new ((_m = (c["a-b"])).bind.apply(_m, [void 0].concat([1, 2], a, ["string"])))(); +new ((_l = (c["a-b"])).bind.apply(_l, [void 0, 1, 2].concat(a)))(); +new ((_m = (c["a-b"])).bind.apply(_m, [void 0, 1, 2].concat(a, ["string"])))(); // Element access expression new g[1]["a-b"](1, 2, "string"); -new ((_o = g[1]["a-b"]).bind.apply(_o, [void 0].concat([1, 2], a)))(); -new ((_p = g[1]["a-b"]).bind.apply(_p, [void 0].concat([1, 2], a, ["string"])))(); +new ((_o = g[1]["a-b"]).bind.apply(_o, [void 0, 1, 2].concat(a)))(); +new ((_p = g[1]["a-b"]).bind.apply(_p, [void 0, 1, 2].concat(a, ["string"])))(); // Element access expression with a punctuated key new h["a-b"]["a-b"](1, 2, "string"); -new ((_q = h["a-b"]["a-b"]).bind.apply(_q, [void 0].concat([1, 2], a)))(); -new ((_r = h["a-b"]["a-b"]).bind.apply(_r, [void 0].concat([1, 2], a, ["string"])))(); +new ((_q = h["a-b"]["a-b"]).bind.apply(_q, [void 0, 1, 2].concat(a)))(); +new ((_r = h["a-b"]["a-b"]).bind.apply(_r, [void 0, 1, 2].concat(a, ["string"])))(); // Element access expression with a number new i["a-b"][1](1, 2, "string"); -new ((_s = i["a-b"][1]).bind.apply(_s, [void 0].concat([1, 2], a)))(); -new ((_t = i["a-b"][1]).bind.apply(_t, [void 0].concat([1, 2], a, ["string"])))(); +new ((_s = i["a-b"][1]).bind.apply(_s, [void 0, 1, 2].concat(a)))(); +new ((_t = i["a-b"][1]).bind.apply(_t, [void 0, 1, 2].concat(a, ["string"])))(); var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;