From 458e87824b0f415e48e74a3792a2399af091881a Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 15 Feb 2017 08:40:23 -0800 Subject: [PATCH] ES5:Emit parameter initialiser before object rest destructuring Fix #14026, where ES5 emit for a parameter with 1. a default value initialiser 2. an object binding pattern containing an object rest incorrectly emitted the destructuring for the object rest before the default value initialisation. This happened because, during emit, the ES next transform runs first, transforming object rest destructuring and marking it as part of the function prologue. Then the ES5 transform runs and transforms the default initialiser, also marking it as part of the prologue. Then the prologue is emitted in the order the statements were added. The fix is to not mark the object rest destructuring as part of the prologue. I'm not 100% sure that this is the right fix, but it fixes the bug as it stands today. Here's an example: ```ts function foobar({ bar={}, ...opts }: any = {}) { } ``` which should have the ES5 emit: ```js function foobar(_a) { if (_a === void 0) { _a = {}; } var _b = _a.bar, bar = _b === void 0 ? {} : _b, opts = __rest(_a, ["bar"]); } ``` --- src/compiler/transformers/esnext.ts | 1 - .../reference/objectRestParameter.js | 12 +- .../reference/objectRestParameter.symbols | 15 +++ .../reference/objectRestParameter.types | 25 +++++ .../reference/objectRestParameterES5.js | 69 ++++++++++++ .../reference/objectRestParameterES5.symbols | 83 ++++++++++++++ .../reference/objectRestParameterES5.types | 104 ++++++++++++++++++ .../types/rest/objectRestParameter.ts | 6 +- .../types/rest/objectRestParameterES5.ts | 21 ++++ 9 files changed, 333 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/objectRestParameterES5.js create mode 100644 tests/baselines/reference/objectRestParameterES5.symbols create mode 100644 tests/baselines/reference/objectRestParameterES5.types create mode 100644 tests/cases/conformance/types/rest/objectRestParameterES5.ts diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index f058e627df7..c413415380d 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -375,7 +375,6 @@ namespace ts { declarations ) ); - setEmitFlags(statement, EmitFlags.CustomPrologue); leadingStatements = append(leadingStatements, statement); } } diff --git a/tests/baselines/reference/objectRestParameter.js b/tests/baselines/reference/objectRestParameter.js index 14e84eddfce..a73e9b77b38 100644 --- a/tests/baselines/reference/objectRestParameter.js +++ b/tests/baselines/reference/objectRestParameter.js @@ -14,7 +14,11 @@ class C { // actually, never mind, don't clone } } - +function foobar({ bar={}, ...opts }: any = {}) { +} +foobar(); +foobar({ baz: 'hello' }); +foobar({ bar: { greeting: 'hello' } }); //// [objectRestParameter.js] @@ -48,3 +52,9 @@ class C { // actually, never mind, don't clone } } +function foobar(_a = {}) { + var { bar = {} } = _a, opts = __rest(_a, ["bar"]); +} +foobar(); +foobar({ baz: 'hello' }); +foobar({ bar: { greeting: 'hello' } }); diff --git a/tests/baselines/reference/objectRestParameter.symbols b/tests/baselines/reference/objectRestParameter.symbols index c43a8ba5a04..f629f46e8e9 100644 --- a/tests/baselines/reference/objectRestParameter.symbols +++ b/tests/baselines/reference/objectRestParameter.symbols @@ -64,5 +64,20 @@ class C { // actually, never mind, don't clone } } +function foobar({ bar={}, ...opts }: any = {}) { +>foobar : Symbol(foobar, Decl(objectRestParameter.ts, 14, 1)) +>bar : Symbol(bar, Decl(objectRestParameter.ts, 15, 17)) +>opts : Symbol(opts, Decl(objectRestParameter.ts, 15, 25)) +} +foobar(); +>foobar : Symbol(foobar, Decl(objectRestParameter.ts, 14, 1)) +foobar({ baz: 'hello' }); +>foobar : Symbol(foobar, Decl(objectRestParameter.ts, 14, 1)) +>baz : Symbol(baz, Decl(objectRestParameter.ts, 18, 8)) + +foobar({ bar: { greeting: 'hello' } }); +>foobar : Symbol(foobar, Decl(objectRestParameter.ts, 14, 1)) +>bar : Symbol(bar, Decl(objectRestParameter.ts, 19, 8)) +>greeting : Symbol(greeting, Decl(objectRestParameter.ts, 19, 15)) diff --git a/tests/baselines/reference/objectRestParameter.types b/tests/baselines/reference/objectRestParameter.types index 56e7352870f..e56e7cebfc7 100644 --- a/tests/baselines/reference/objectRestParameter.types +++ b/tests/baselines/reference/objectRestParameter.types @@ -75,5 +75,30 @@ class C { // actually, never mind, don't clone } } +function foobar({ bar={}, ...opts }: any = {}) { +>foobar : ({bar, ...opts}?: any) => void +>bar : {} +>{} : {} +>opts : any +>{} : {} +} +foobar(); +>foobar() : void +>foobar : ({bar, ...opts}?: any) => void +foobar({ baz: 'hello' }); +>foobar({ baz: 'hello' }) : void +>foobar : ({bar, ...opts}?: any) => void +>{ baz: 'hello' } : { baz: string; } +>baz : string +>'hello' : "hello" + +foobar({ bar: { greeting: 'hello' } }); +>foobar({ bar: { greeting: 'hello' } }) : void +>foobar : ({bar, ...opts}?: any) => void +>{ bar: { greeting: 'hello' } } : { bar: { greeting: string; }; } +>bar : { greeting: string; } +>{ greeting: 'hello' } : { greeting: string; } +>greeting : string +>'hello' : "hello" diff --git a/tests/baselines/reference/objectRestParameterES5.js b/tests/baselines/reference/objectRestParameterES5.js new file mode 100644 index 00000000000..d16a0c68df0 --- /dev/null +++ b/tests/baselines/reference/objectRestParameterES5.js @@ -0,0 +1,69 @@ +//// [objectRestParameterES5.ts] +function cloneAgain({ a, ...clone }: { a: number, b: string }): void { +} + +declare function suddenly(f: (a: { x: { z, ka }, y: string }) => void); +suddenly(({ x: a, ...rest }) => rest.y); +suddenly(({ x: { z = 12, ...nested }, ...rest } = { x: { z: 1, ka: 1 }, y: 'noo' }) => rest.y + nested.ka); + +class C { + m({ a, ...clone }: { a: number, b: string}): void { + // actually, never mind, don't clone + } + set p({ a, ...clone }: { a: number, b: string}) { + // actually, never mind, don't clone + } +} +function foobar({ bar={}, ...opts }: any = {}) { +} +foobar(); +foobar({ baz: 'hello' }); +foobar({ bar: { greeting: 'hello' } }); + + +//// [objectRestParameterES5.js] +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) + t[p[i]] = s[p[i]]; + return t; +}; +function cloneAgain(_a) { + var a = _a.a, clone = __rest(_a, ["a"]); +} +suddenly(function (_a) { + var a = _a.x, rest = __rest(_a, ["x"]); + return rest.y; +}); +suddenly(function (_a) { + if (_a === void 0) { _a = { x: { z: 1, ka: 1 }, y: 'noo' }; } + var _b = _a.x, _c = _b.z, z = _c === void 0 ? 12 : _c, nested = __rest(_b, ["z"]), rest = __rest(_a, ["x"]); + return rest.y + nested.ka; +}); +var C = (function () { + function C() { + } + C.prototype.m = function (_a) { + var a = _a.a, clone = __rest(_a, ["a"]); + // actually, never mind, don't clone + }; + Object.defineProperty(C.prototype, "p", { + set: function (_a) { + var a = _a.a, clone = __rest(_a, ["a"]); + // actually, never mind, don't clone + }, + enumerable: true, + configurable: true + }); + return C; +}()); +function foobar(_a) { + if (_a === void 0) { _a = {}; } + var _b = _a.bar, bar = _b === void 0 ? {} : _b, opts = __rest(_a, ["bar"]); +} +foobar(); +foobar({ baz: 'hello' }); +foobar({ bar: { greeting: 'hello' } }); diff --git a/tests/baselines/reference/objectRestParameterES5.symbols b/tests/baselines/reference/objectRestParameterES5.symbols new file mode 100644 index 00000000000..4c6b8115169 --- /dev/null +++ b/tests/baselines/reference/objectRestParameterES5.symbols @@ -0,0 +1,83 @@ +=== tests/cases/conformance/types/rest/objectRestParameterES5.ts === +function cloneAgain({ a, ...clone }: { a: number, b: string }): void { +>cloneAgain : Symbol(cloneAgain, Decl(objectRestParameterES5.ts, 0, 0)) +>a : Symbol(a, Decl(objectRestParameterES5.ts, 0, 21)) +>clone : Symbol(clone, Decl(objectRestParameterES5.ts, 0, 24)) +>a : Symbol(a, Decl(objectRestParameterES5.ts, 0, 38)) +>b : Symbol(b, Decl(objectRestParameterES5.ts, 0, 49)) +} + +declare function suddenly(f: (a: { x: { z, ka }, y: string }) => void); +>suddenly : Symbol(suddenly, Decl(objectRestParameterES5.ts, 1, 1)) +>f : Symbol(f, Decl(objectRestParameterES5.ts, 3, 26)) +>a : Symbol(a, Decl(objectRestParameterES5.ts, 3, 30)) +>x : Symbol(x, Decl(objectRestParameterES5.ts, 3, 34)) +>z : Symbol(z, Decl(objectRestParameterES5.ts, 3, 39)) +>ka : Symbol(ka, Decl(objectRestParameterES5.ts, 3, 42)) +>y : Symbol(y, Decl(objectRestParameterES5.ts, 3, 48)) + +suddenly(({ x: a, ...rest }) => rest.y); +>suddenly : Symbol(suddenly, Decl(objectRestParameterES5.ts, 1, 1)) +>x : Symbol(x, Decl(objectRestParameterES5.ts, 3, 34)) +>a : Symbol(a, Decl(objectRestParameterES5.ts, 4, 11)) +>rest : Symbol(rest, Decl(objectRestParameterES5.ts, 4, 17)) +>rest.y : Symbol(y, Decl(objectRestParameterES5.ts, 3, 48)) +>rest : Symbol(rest, Decl(objectRestParameterES5.ts, 4, 17)) +>y : Symbol(y, Decl(objectRestParameterES5.ts, 3, 48)) + +suddenly(({ x: { z = 12, ...nested }, ...rest } = { x: { z: 1, ka: 1 }, y: 'noo' }) => rest.y + nested.ka); +>suddenly : Symbol(suddenly, Decl(objectRestParameterES5.ts, 1, 1)) +>x : Symbol(x, Decl(objectRestParameterES5.ts, 3, 34)) +>z : Symbol(z, Decl(objectRestParameterES5.ts, 5, 16)) +>nested : Symbol(nested, Decl(objectRestParameterES5.ts, 5, 24)) +>rest : Symbol(rest, Decl(objectRestParameterES5.ts, 5, 37)) +>x : Symbol(x, Decl(objectRestParameterES5.ts, 5, 51)) +>z : Symbol(z, Decl(objectRestParameterES5.ts, 5, 56)) +>ka : Symbol(ka, Decl(objectRestParameterES5.ts, 5, 62)) +>y : Symbol(y, Decl(objectRestParameterES5.ts, 5, 71)) +>rest.y : Symbol(y, Decl(objectRestParameterES5.ts, 3, 48)) +>rest : Symbol(rest, Decl(objectRestParameterES5.ts, 5, 37)) +>y : Symbol(y, Decl(objectRestParameterES5.ts, 3, 48)) +>nested.ka : Symbol(ka, Decl(objectRestParameterES5.ts, 3, 42)) +>nested : Symbol(nested, Decl(objectRestParameterES5.ts, 5, 24)) +>ka : Symbol(ka, Decl(objectRestParameterES5.ts, 3, 42)) + +class C { +>C : Symbol(C, Decl(objectRestParameterES5.ts, 5, 107)) + + m({ a, ...clone }: { a: number, b: string}): void { +>m : Symbol(C.m, Decl(objectRestParameterES5.ts, 7, 9)) +>a : Symbol(a, Decl(objectRestParameterES5.ts, 8, 7)) +>clone : Symbol(clone, Decl(objectRestParameterES5.ts, 8, 10)) +>a : Symbol(a, Decl(objectRestParameterES5.ts, 8, 24)) +>b : Symbol(b, Decl(objectRestParameterES5.ts, 8, 35)) + + // actually, never mind, don't clone + } + set p({ a, ...clone }: { a: number, b: string}) { +>p : Symbol(C.p, Decl(objectRestParameterES5.ts, 10, 5)) +>a : Symbol(a, Decl(objectRestParameterES5.ts, 11, 11)) +>clone : Symbol(clone, Decl(objectRestParameterES5.ts, 11, 14)) +>a : Symbol(a, Decl(objectRestParameterES5.ts, 11, 28)) +>b : Symbol(b, Decl(objectRestParameterES5.ts, 11, 39)) + + // actually, never mind, don't clone + } +} +function foobar({ bar={}, ...opts }: any = {}) { +>foobar : Symbol(foobar, Decl(objectRestParameterES5.ts, 14, 1)) +>bar : Symbol(bar, Decl(objectRestParameterES5.ts, 15, 17)) +>opts : Symbol(opts, Decl(objectRestParameterES5.ts, 15, 25)) +} +foobar(); +>foobar : Symbol(foobar, Decl(objectRestParameterES5.ts, 14, 1)) + +foobar({ baz: 'hello' }); +>foobar : Symbol(foobar, Decl(objectRestParameterES5.ts, 14, 1)) +>baz : Symbol(baz, Decl(objectRestParameterES5.ts, 18, 8)) + +foobar({ bar: { greeting: 'hello' } }); +>foobar : Symbol(foobar, Decl(objectRestParameterES5.ts, 14, 1)) +>bar : Symbol(bar, Decl(objectRestParameterES5.ts, 19, 8)) +>greeting : Symbol(greeting, Decl(objectRestParameterES5.ts, 19, 15)) + diff --git a/tests/baselines/reference/objectRestParameterES5.types b/tests/baselines/reference/objectRestParameterES5.types new file mode 100644 index 00000000000..009d810782b --- /dev/null +++ b/tests/baselines/reference/objectRestParameterES5.types @@ -0,0 +1,104 @@ +=== tests/cases/conformance/types/rest/objectRestParameterES5.ts === +function cloneAgain({ a, ...clone }: { a: number, b: string }): void { +>cloneAgain : ({a, ...clone}: { a: number; b: string; }) => void +>a : number +>clone : { b: string; } +>a : number +>b : string +} + +declare function suddenly(f: (a: { x: { z, ka }, y: string }) => void); +>suddenly : (f: (a: { x: { z: any; ka: any; }; y: string; }) => void) => any +>f : (a: { x: { z: any; ka: any; }; y: string; }) => void +>a : { x: { z: any; ka: any; }; y: string; } +>x : { z: any; ka: any; } +>z : any +>ka : any +>y : string + +suddenly(({ x: a, ...rest }) => rest.y); +>suddenly(({ x: a, ...rest }) => rest.y) : any +>suddenly : (f: (a: { x: { z: any; ka: any; }; y: string; }) => void) => any +>({ x: a, ...rest }) => rest.y : ({x: a, ...rest}: { x: { z: any; ka: any; }; y: string; }) => string +>x : any +>a : { z: any; ka: any; } +>rest : { y: string; } +>rest.y : string +>rest : { y: string; } +>y : string + +suddenly(({ x: { z = 12, ...nested }, ...rest } = { x: { z: 1, ka: 1 }, y: 'noo' }) => rest.y + nested.ka); +>suddenly(({ x: { z = 12, ...nested }, ...rest } = { x: { z: 1, ka: 1 }, y: 'noo' }) => rest.y + nested.ka) : any +>suddenly : (f: (a: { x: { z: any; ka: any; }; y: string; }) => void) => any +>({ x: { z = 12, ...nested }, ...rest } = { x: { z: 1, ka: 1 }, y: 'noo' }) => rest.y + nested.ka : ({x: {z, ...nested}, ...rest}?: { x: { z: any; ka: any; }; y: string; }) => string +>x : any +>z : any +>12 : 12 +>nested : { ka: any; } +>rest : { y: string; } +>{ x: { z: 1, ka: 1 }, y: 'noo' } : { x: { z: number; ka: number; }; y: string; } +>x : { z: number; ka: number; } +>{ z: 1, ka: 1 } : { z: number; ka: number; } +>z : number +>1 : 1 +>ka : number +>1 : 1 +>y : string +>'noo' : "noo" +>rest.y + nested.ka : string +>rest.y : string +>rest : { y: string; } +>y : string +>nested.ka : any +>nested : { ka: any; } +>ka : any + +class C { +>C : C + + m({ a, ...clone }: { a: number, b: string}): void { +>m : ({a, ...clone}: { a: number; b: string; }) => void +>a : number +>clone : { b: string; } +>a : number +>b : string + + // actually, never mind, don't clone + } + set p({ a, ...clone }: { a: number, b: string}) { +>p : { a: number; b: string; } +>a : number +>clone : { b: string; } +>a : number +>b : string + + // actually, never mind, don't clone + } +} +function foobar({ bar={}, ...opts }: any = {}) { +>foobar : ({bar, ...opts}?: any) => void +>bar : {} +>{} : {} +>opts : any +>{} : {} +} +foobar(); +>foobar() : void +>foobar : ({bar, ...opts}?: any) => void + +foobar({ baz: 'hello' }); +>foobar({ baz: 'hello' }) : void +>foobar : ({bar, ...opts}?: any) => void +>{ baz: 'hello' } : { baz: string; } +>baz : string +>'hello' : "hello" + +foobar({ bar: { greeting: 'hello' } }); +>foobar({ bar: { greeting: 'hello' } }) : void +>foobar : ({bar, ...opts}?: any) => void +>{ bar: { greeting: 'hello' } } : { bar: { greeting: string; }; } +>bar : { greeting: string; } +>{ greeting: 'hello' } : { greeting: string; } +>greeting : string +>'hello' : "hello" + diff --git a/tests/cases/conformance/types/rest/objectRestParameter.ts b/tests/cases/conformance/types/rest/objectRestParameter.ts index a9c17a29d14..5b6faeb7978 100644 --- a/tests/cases/conformance/types/rest/objectRestParameter.ts +++ b/tests/cases/conformance/types/rest/objectRestParameter.ts @@ -14,4 +14,8 @@ class C { // actually, never mind, don't clone } } - +function foobar({ bar={}, ...opts }: any = {}) { +} +foobar(); +foobar({ baz: 'hello' }); +foobar({ bar: { greeting: 'hello' } }); diff --git a/tests/cases/conformance/types/rest/objectRestParameterES5.ts b/tests/cases/conformance/types/rest/objectRestParameterES5.ts new file mode 100644 index 00000000000..07a15ffbd44 --- /dev/null +++ b/tests/cases/conformance/types/rest/objectRestParameterES5.ts @@ -0,0 +1,21 @@ +// @target: es5 +function cloneAgain({ a, ...clone }: { a: number, b: string }): void { +} + +declare function suddenly(f: (a: { x: { z, ka }, y: string }) => void); +suddenly(({ x: a, ...rest }) => rest.y); +suddenly(({ x: { z = 12, ...nested }, ...rest } = { x: { z: 1, ka: 1 }, y: 'noo' }) => rest.y + nested.ka); + +class C { + m({ a, ...clone }: { a: number, b: string}): void { + // actually, never mind, don't clone + } + set p({ a, ...clone }: { a: number, b: string}) { + // actually, never mind, don't clone + } +} +function foobar({ bar={}, ...opts }: any = {}) { +} +foobar(); +foobar({ baz: 'hello' }); +foobar({ bar: { greeting: 'hello' } });