From 458e87824b0f415e48e74a3792a2399af091881a Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 15 Feb 2017 08:40:23 -0800 Subject: [PATCH 1/5] 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' } }); From 304864c626a7a3fe78ad4f167bef361aaae89744 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 15 Feb 2017 13:29:02 -0800 Subject: [PATCH 2/5] Change prologue generation order; object rest back in prologue --- src/compiler/factory.ts | 1 - src/compiler/transformers/es2015.ts | 21 +++++++++------------ src/compiler/transformers/esnext.ts | 1 + 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index d5d8482a0ba..5f94ae8931a 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2599,7 +2599,6 @@ namespace ts { * @param visitor: Optional callback used to visit any custom prologue directives. */ export function addPrologueDirectives(target: Statement[], source: Statement[], ensureUseStrict?: boolean, visitor?: (node: Node) => VisitResult): number { - Debug.assert(target.length === 0, "Prologue directives should be at the first statement in the target statements array"); let foundUseStrict = false; let statementOffset = 0; const numStatements = source.length; diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index e0fd435a90d..8353e393aa4 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -507,8 +507,8 @@ namespace ts { const ancestorFacts = enterSubtree(HierarchyFacts.SourceFileExcludes, HierarchyFacts.SourceFileIncludes); const statements: Statement[] = []; startLexicalEnvironment(); - const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ false, visitor); addCaptureThisForNodeIfNeeded(statements, node); + const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ false, visitor); addRange(statements, visitNodes(node.statements, visitor, isStatement, statementOffset)); addRange(statements, endLexicalEnvironment()); exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None); @@ -915,14 +915,14 @@ namespace ts { // The assumption is that no prior step in the pipeline has added any prologue directives. statementOffset = 0; } - else if (constructor) { - // Otherwise, try to emit all potential prologue directives first. - statementOffset = addPrologueDirectives(statements, constructor.body.statements, /*ensureUseStrict*/ false, visitor); - } if (constructor) { addDefaultValueAssignmentsIfNeeded(statements, constructor); addRestParameterIfNeeded(statements, constructor, hasSynthesizedSuper); + if (!hasSynthesizedSuper) { + // If no super call has been synthesized, try to emit all potential prologue directives. + statementOffset = addPrologueDirectives(statements, constructor.body.statements, /*ensureUseStrict*/ false, visitor); + } Debug.assert(statementOffset >= 0, "statementOffset not initialized correctly!"); } @@ -1806,15 +1806,8 @@ namespace ts { const statements: Statement[] = []; const body = node.body; - let statementOffset: number; resumeLexicalEnvironment(); - if (isBlock(body)) { - // ensureUseStrict is false because no new prologue-directive should be added. - // addPrologueDirectives will simply put already-existing directives at the beginning of the target statement-array - statementOffset = addPrologueDirectives(statements, body.statements, /*ensureUseStrict*/ false, visitor); - } - addCaptureThisForNodeIfNeeded(statements, node); addDefaultValueAssignmentsIfNeeded(statements, node); addRestParameterIfNeeded(statements, node, /*inConstructorWithSynthesizedSuper*/ false); @@ -1825,6 +1818,10 @@ namespace ts { } if (isBlock(body)) { + // ensureUseStrict is false because no new prologue-directive should be added. + // addPrologueDirectives will simply put already-existing directives at the beginning of the target statement-array + const statementOffset = addPrologueDirectives(statements, body.statements, /*ensureUseStrict*/ false, visitor); + statementsLocation = body.statements; addRange(statements, visitNodes(body.statements, visitor, isStatement, statementOffset)); diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index c413415380d..f058e627df7 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -375,6 +375,7 @@ namespace ts { declarations ) ); + setEmitFlags(statement, EmitFlags.CustomPrologue); leadingStatements = append(leadingStatements, statement); } } From 5496096ee36c4305007639df424ab9f04d4ea1ab Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 13 Apr 2017 10:29:32 -0700 Subject: [PATCH 3/5] Split addPrologue into string + custom directives The original function is now named addPrologue. There are then three places than need to call the two split functions separately. --- src/compiler/factory.ts | 27 ++++++++++++++++++++-- src/compiler/transformers/es2015.ts | 22 +++++++++++++----- src/compiler/transformers/es2017.ts | 2 +- src/compiler/transformers/esnext.ts | 15 ++++++++---- src/compiler/transformers/generators.ts | 2 +- src/compiler/transformers/module/es2015.ts | 2 +- src/compiler/transformers/module/module.ts | 4 ++-- src/compiler/transformers/module/system.ts | 2 +- src/compiler/transformers/ts.ts | 2 +- 9 files changed, 59 insertions(+), 19 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 5a4c4c032ee..a945f7c7c10 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -3058,14 +3058,26 @@ namespace ts { * @param ensureUseStrict: boolean determining whether the function need to add prologue-directives * @param visitor: Optional callback used to visit any custom prologue directives. */ - export function addPrologueDirectives(target: Statement[], source: Statement[], ensureUseStrict?: boolean, visitor?: (node: Node) => VisitResult): number { + export function addPrologue(target: Statement[], source: Statement[], ensureUseStrict?: boolean, visitor?: (node: Node) => VisitResult): number { + const offset = addPrologueDirectives(target, source, ensureUseStrict); + return addCustomPrologue(target, source, offset, visitor); + } + + /** + * Add just the standard (string-expression) prologue-directives into target statement-array. + * The function needs to be called during each transformation step. + * This function needs to be called whenever we transform the statement + * list of a source file, namespace, or function-like body. + */ + export function addPrologueDirectives(target: Statement[], source: Statement[], ensureUseStrict?: boolean): number { + Debug.assert(target.length === 0, "Prologue directives should be at the first statement in the target statements array"); let foundUseStrict = false; let statementOffset = 0; const numStatements = source.length; while (statementOffset < numStatements) { const statement = source[statementOffset]; if (isPrologueDirective(statement)) { - if (isUseStrictPrologue(statement as ExpressionStatement)) { + if (isUseStrictPrologue(statement)) { foundUseStrict = true; } target.push(statement); @@ -3078,6 +3090,17 @@ namespace ts { if (ensureUseStrict && !foundUseStrict) { target.push(startOnNewLine(createStatement(createLiteral("use strict")))); } + return statementOffset; + } + + /** + * Add just the custom prologue-directives into target statement-array. + * The function needs to be called during each transformation step. + * This function needs to be called whenever we transform the statement + * list of a source file, namespace, or function-like body. + */ + export function addCustomPrologue(target: Statement[], source: Statement[], statementOffset: number, visitor?: (node: Node) => VisitResult): number { + const numStatements = source.length; while (statementOffset < numStatements) { const statement = source[statementOffset]; if (getEmitFlags(statement) & EmitFlags.CustomPrologue) { diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 3675571b720..897a4a57502 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -515,8 +515,9 @@ namespace ts { const ancestorFacts = enterSubtree(HierarchyFacts.SourceFileExcludes, HierarchyFacts.SourceFileIncludes); const statements: Statement[] = []; startLexicalEnvironment(); + let statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ false); addCaptureThisForNodeIfNeeded(statements, node); - const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ false, visitor); + statementOffset = addCustomPrologue(statements, node.statements, statementOffset, visitor); addRange(statements, visitNodes(node.statements, visitor, isStatement, statementOffset)); addRange(statements, endLexicalEnvironment()); exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None); @@ -923,13 +924,16 @@ namespace ts { // The assumption is that no prior step in the pipeline has added any prologue directives. statementOffset = 0; } + else if (constructor) { + statementOffset = addPrologueDirectives(statements, constructor.body.statements, /*ensureUseStrict*/ false); + } if (constructor) { addDefaultValueAssignmentsIfNeeded(statements, constructor); addRestParameterIfNeeded(statements, constructor, hasSynthesizedSuper); if (!hasSynthesizedSuper) { - // If no super call has been synthesized, try to emit all potential prologue directives. - statementOffset = addPrologueDirectives(statements, constructor.body.statements, /*ensureUseStrict*/ false, visitor); + // If no super call has been synthesized, emit custom prologue directives. + statementOffset = addCustomPrologue(statements, constructor.body.statements, statementOffset, visitor); } Debug.assert(statementOffset >= 0, "statementOffset not initialized correctly!"); @@ -1816,8 +1820,15 @@ namespace ts { const statements: Statement[] = []; const body = node.body; + let statementOffset: number; resumeLexicalEnvironment(); + if (isBlock(body)) { + // ensureUseStrict is false because no new prologue-directive should be added. + // addPrologueDirectives will put already-existing directives at the beginning of the target statement-array + statementOffset = addPrologueDirectives(statements, body.statements, /*ensureUseStrict*/ false); + } + addCaptureThisForNodeIfNeeded(statements, node); addDefaultValueAssignmentsIfNeeded(statements, node); addRestParameterIfNeeded(statements, node, /*inConstructorWithSynthesizedSuper*/ false); @@ -1828,9 +1839,8 @@ namespace ts { } if (isBlock(body)) { - // ensureUseStrict is false because no new prologue-directive should be added. - // addPrologueDirectives will simply put already-existing directives at the beginning of the target statement-array - const statementOffset = addPrologueDirectives(statements, body.statements, /*ensureUseStrict*/ false, visitor); + // addCustomPrologue puts already-existing directives at the beginning of the target statement-array + statementOffset = addCustomPrologue(statements, body.statements, statementOffset, visitor); statementsLocation = body.statements; addRange(statements, visitNodes(body.statements, visitor, isStatement, statementOffset)); diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 088c57866d0..3bbb3b3123f 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -222,7 +222,7 @@ namespace ts { if (!isArrowFunction) { const statements: Statement[] = []; - const statementOffset = addPrologueDirectives(statements, (node.body).statements, /*ensureUseStrict*/ false, visitor); + const statementOffset = addPrologue(statements, (node.body).statements, /*ensureUseStrict*/ false, visitor); statements.push( createReturn( createAwaiterHelper( diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 83e84e6eab5..b0e8b527820 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -618,7 +618,7 @@ namespace ts { function transformAsyncGeneratorFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody { resumeLexicalEnvironment(); const statements: Statement[] = []; - const statementOffset = addPrologueDirectives(statements, node.body.statements, /*ensureUseStrict*/ false, visitor); + const statementOffset = addPrologue(statements, node.body.statements, /*ensureUseStrict*/ false, visitor); appendObjectRestAssignmentsIfNeeded(statements, node); statements.push( @@ -663,12 +663,19 @@ namespace ts { function transformFunctionBody(node: ArrowFunction): ConciseBody; function transformFunctionBody(node: FunctionLikeDeclaration): ConciseBody { resumeLexicalEnvironment(); - const leadingStatements = appendObjectRestAssignmentsIfNeeded(/*statements*/ undefined, node); + let statementOffset = 0; + const statements: Statement[] = []; const body = visitNode(node.body, visitor, isConciseBody); + if (isBlock(body)) { + statementOffset = addPrologue(statements, body.statements, /*ensureUseStrict*/ false, visitor); + } + addRange(statements, appendObjectRestAssignmentsIfNeeded(/*statements*/ undefined, node)); const trailingStatements = endLexicalEnvironment(); - if (some(leadingStatements) || some(trailingStatements)) { + if (statementOffset > 0 || some(statements) || some(trailingStatements)) { const block = convertToFunctionBody(body, /*multiLine*/ true); - return updateBlock(block, setTextRange(createNodeArray(concatenate(concatenate(leadingStatements, block.statements), trailingStatements)), block.statements)); + addRange(statements, block.statements.slice(statementOffset)); + addRange(statements, trailingStatements); + return updateBlock(block, setTextRange(createNodeArray(statements), block.statements)); } return body; } diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index d2f2162f455..3b361b1d524 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -587,7 +587,7 @@ namespace ts { // Build the generator resumeLexicalEnvironment(); - const statementOffset = addPrologueDirectives(statements, body.statements, /*ensureUseStrict*/ false, visitor); + const statementOffset = addPrologue(statements, body.statements, /*ensureUseStrict*/ false, visitor); transformAndEmitStatements(body.statements, statementOffset); diff --git a/src/compiler/transformers/module/es2015.ts b/src/compiler/transformers/module/es2015.ts index 7028e235961..660293e074f 100644 --- a/src/compiler/transformers/module/es2015.ts +++ b/src/compiler/transformers/module/es2015.ts @@ -24,7 +24,7 @@ namespace ts { const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(node, compilerOptions); if (externalHelpersModuleName) { const statements: Statement[] = []; - const statementOffset = addPrologueDirectives(statements, node.statements); + const statementOffset = addPrologue(statements, node.statements); append(statements, createImportDeclaration( /*decorators*/ undefined, diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 4fe7b027e93..d387a1a3a24 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -90,7 +90,7 @@ namespace ts { const statements: Statement[] = []; const ensureUseStrict = compilerOptions.alwaysStrict || (!compilerOptions.noImplicitUseStrict && isExternalModule(currentSourceFile)); - const statementOffset = addPrologueDirectives(statements, node.statements, ensureUseStrict, sourceElementVisitor); + const statementOffset = addPrologue(statements, node.statements, ensureUseStrict, sourceElementVisitor); if (shouldEmitUnderscoreUnderscoreESModule()) { append(statements, createUnderscoreUnderscoreESModule()); @@ -388,7 +388,7 @@ namespace ts { startLexicalEnvironment(); const statements: Statement[] = []; - const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ !compilerOptions.noImplicitUseStrict, sourceElementVisitor); + const statementOffset = addPrologue(statements, node.statements, /*ensureUseStrict*/ !compilerOptions.noImplicitUseStrict, sourceElementVisitor); if (shouldEmitUnderscoreUnderscoreESModule()) { append(statements, createUnderscoreUnderscoreESModule()); diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 56341f482ff..cde4fcad57e 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -228,7 +228,7 @@ namespace ts { // Add any prologue directives. const ensureUseStrict = compilerOptions.alwaysStrict || (!compilerOptions.noImplicitUseStrict && isExternalModule(currentSourceFile)); - const statementOffset = addPrologueDirectives(statements, node.statements, ensureUseStrict, sourceElementVisitor); + const statementOffset = addPrologue(statements, node.statements, ensureUseStrict, sourceElementVisitor); // var __moduleName = context_1 && context_1.id; statements.push( diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 8df760d51c0..0daf76fa9ec 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -954,7 +954,7 @@ namespace ts { if (ctor.body) { const statements = ctor.body.statements; // add prologue directives to the list (if any) - const index = addPrologueDirectives(result, statements, /*ensureUseStrict*/ false, visitor); + const index = addPrologue(result, statements, /*ensureUseStrict*/ false, visitor); if (index === statements.length) { // list contains nothing but prologue directives (or empty) - exit return index; From b2199bd23c98bc4cb29024c9c93f4c62166ef671 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 13 Apr 2017 10:35:50 -0700 Subject: [PATCH 4/5] Test:order of directives, initialisers, object spread destructuring --- ...meterInitializerBeforeDestructuringEmit.js | 49 ++++++++++++++++ ...InitializerBeforeDestructuringEmit.symbols | 44 ++++++++++++++ ...erInitializerBeforeDestructuringEmit.types | 58 +++++++++++++++++++ ...meterInitializerBeforeDestructuringEmit.ts | 20 +++++++ 4 files changed, 171 insertions(+) create mode 100644 tests/baselines/reference/parameterInitializerBeforeDestructuringEmit.js create mode 100644 tests/baselines/reference/parameterInitializerBeforeDestructuringEmit.symbols create mode 100644 tests/baselines/reference/parameterInitializerBeforeDestructuringEmit.types create mode 100644 tests/cases/compiler/parameterInitializerBeforeDestructuringEmit.ts diff --git a/tests/baselines/reference/parameterInitializerBeforeDestructuringEmit.js b/tests/baselines/reference/parameterInitializerBeforeDestructuringEmit.js new file mode 100644 index 00000000000..e7d4a4472e8 --- /dev/null +++ b/tests/baselines/reference/parameterInitializerBeforeDestructuringEmit.js @@ -0,0 +1,49 @@ +//// [parameterInitializerBeforeDestructuringEmit.ts] +interface Foo { + bar?: any; + baz?: any; +} + +function foobar({ bar = {}, ...opts }: Foo = {}) { + "use strict"; + "Some other prologue"; + opts.baz(bar); +} + +class C { + constructor({ bar = {}, ...opts }: Foo = {}) { + "use strict"; + "Some other prologue"; + opts.baz(bar); + } +} + + +//// [parameterInitializerBeforeDestructuringEmit.js] +"use strict"; +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 foobar(_a) { + "use strict"; + "Some other prologue"; + if (_a === void 0) { _a = {}; } + var _b = _a.bar, bar = _b === void 0 ? {} : _b, opts = __rest(_a, ["bar"]); + opts.baz(bar); +} +var C = (function () { + function C(_a) { + "use strict"; + "Some other prologue"; + if (_a === void 0) { _a = {}; } + var _b = _a.bar, bar = _b === void 0 ? {} : _b, opts = __rest(_a, ["bar"]); + opts.baz(bar); + } + return C; +}()); diff --git a/tests/baselines/reference/parameterInitializerBeforeDestructuringEmit.symbols b/tests/baselines/reference/parameterInitializerBeforeDestructuringEmit.symbols new file mode 100644 index 00000000000..857310bf87c --- /dev/null +++ b/tests/baselines/reference/parameterInitializerBeforeDestructuringEmit.symbols @@ -0,0 +1,44 @@ +=== tests/cases/compiler/parameterInitializerBeforeDestructuringEmit.ts === +interface Foo { +>Foo : Symbol(Foo, Decl(parameterInitializerBeforeDestructuringEmit.ts, 0, 0)) + + bar?: any; +>bar : Symbol(Foo.bar, Decl(parameterInitializerBeforeDestructuringEmit.ts, 0, 15)) + + baz?: any; +>baz : Symbol(Foo.baz, Decl(parameterInitializerBeforeDestructuringEmit.ts, 1, 14)) +} + +function foobar({ bar = {}, ...opts }: Foo = {}) { +>foobar : Symbol(foobar, Decl(parameterInitializerBeforeDestructuringEmit.ts, 3, 1)) +>bar : Symbol(bar, Decl(parameterInitializerBeforeDestructuringEmit.ts, 5, 17)) +>opts : Symbol(opts, Decl(parameterInitializerBeforeDestructuringEmit.ts, 5, 27)) +>Foo : Symbol(Foo, Decl(parameterInitializerBeforeDestructuringEmit.ts, 0, 0)) + + "use strict"; + "Some other prologue"; + opts.baz(bar); +>opts.baz : Symbol(Foo.baz, Decl(parameterInitializerBeforeDestructuringEmit.ts, 1, 14)) +>opts : Symbol(opts, Decl(parameterInitializerBeforeDestructuringEmit.ts, 5, 27)) +>baz : Symbol(Foo.baz, Decl(parameterInitializerBeforeDestructuringEmit.ts, 1, 14)) +>bar : Symbol(bar, Decl(parameterInitializerBeforeDestructuringEmit.ts, 5, 17)) +} + +class C { +>C : Symbol(C, Decl(parameterInitializerBeforeDestructuringEmit.ts, 9, 1)) + + constructor({ bar = {}, ...opts }: Foo = {}) { +>bar : Symbol(bar, Decl(parameterInitializerBeforeDestructuringEmit.ts, 12, 17)) +>opts : Symbol(opts, Decl(parameterInitializerBeforeDestructuringEmit.ts, 12, 27)) +>Foo : Symbol(Foo, Decl(parameterInitializerBeforeDestructuringEmit.ts, 0, 0)) + + "use strict"; + "Some other prologue"; + opts.baz(bar); +>opts.baz : Symbol(Foo.baz, Decl(parameterInitializerBeforeDestructuringEmit.ts, 1, 14)) +>opts : Symbol(opts, Decl(parameterInitializerBeforeDestructuringEmit.ts, 12, 27)) +>baz : Symbol(Foo.baz, Decl(parameterInitializerBeforeDestructuringEmit.ts, 1, 14)) +>bar : Symbol(bar, Decl(parameterInitializerBeforeDestructuringEmit.ts, 12, 17)) + } +} + diff --git a/tests/baselines/reference/parameterInitializerBeforeDestructuringEmit.types b/tests/baselines/reference/parameterInitializerBeforeDestructuringEmit.types new file mode 100644 index 00000000000..46b5983a514 --- /dev/null +++ b/tests/baselines/reference/parameterInitializerBeforeDestructuringEmit.types @@ -0,0 +1,58 @@ +=== tests/cases/compiler/parameterInitializerBeforeDestructuringEmit.ts === +interface Foo { +>Foo : Foo + + bar?: any; +>bar : any + + baz?: any; +>baz : any +} + +function foobar({ bar = {}, ...opts }: Foo = {}) { +>foobar : ({bar, ...opts}?: Foo) => void +>bar : any +>{} : {} +>opts : { baz?: any; } +>Foo : Foo +>{} : {} + + "use strict"; +>"use strict" : "use strict" + + "Some other prologue"; +>"Some other prologue" : "Some other prologue" + + opts.baz(bar); +>opts.baz(bar) : any +>opts.baz : any +>opts : { baz?: any; } +>baz : any +>bar : any +} + +class C { +>C : C + + constructor({ bar = {}, ...opts }: Foo = {}) { +>bar : any +>{} : {} +>opts : { baz?: any; } +>Foo : Foo +>{} : {} + + "use strict"; +>"use strict" : "use strict" + + "Some other prologue"; +>"Some other prologue" : "Some other prologue" + + opts.baz(bar); +>opts.baz(bar) : any +>opts.baz : any +>opts : { baz?: any; } +>baz : any +>bar : any + } +} + diff --git a/tests/cases/compiler/parameterInitializerBeforeDestructuringEmit.ts b/tests/cases/compiler/parameterInitializerBeforeDestructuringEmit.ts new file mode 100644 index 00000000000..461ab1b9cf8 --- /dev/null +++ b/tests/cases/compiler/parameterInitializerBeforeDestructuringEmit.ts @@ -0,0 +1,20 @@ +// @noImplicitUseStrict: false +// @alwaysStrict: true +interface Foo { + bar?: any; + baz?: any; +} + +function foobar({ bar = {}, ...opts }: Foo = {}) { + "use strict"; + "Some other prologue"; + opts.baz(bar); +} + +class C { + constructor({ bar = {}, ...opts }: Foo = {}) { + "use strict"; + "Some other prologue"; + opts.baz(bar); + } +} From 58a6c92c297a7bb9da2d9e57c8440993b74ff3e8 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 13 Apr 2017 13:21:56 -0700 Subject: [PATCH 5/5] Rename addPrologueDirectives->addStandardPrologue --- src/compiler/factory.ts | 4 ++-- src/compiler/transformers/es2015.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index a945f7c7c10..5c7486debe3 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -3059,7 +3059,7 @@ namespace ts { * @param visitor: Optional callback used to visit any custom prologue directives. */ export function addPrologue(target: Statement[], source: Statement[], ensureUseStrict?: boolean, visitor?: (node: Node) => VisitResult): number { - const offset = addPrologueDirectives(target, source, ensureUseStrict); + const offset = addStandardPrologue(target, source, ensureUseStrict); return addCustomPrologue(target, source, offset, visitor); } @@ -3069,7 +3069,7 @@ namespace ts { * This function needs to be called whenever we transform the statement * list of a source file, namespace, or function-like body. */ - export function addPrologueDirectives(target: Statement[], source: Statement[], ensureUseStrict?: boolean): number { + export function addStandardPrologue(target: Statement[], source: Statement[], ensureUseStrict?: boolean): number { Debug.assert(target.length === 0, "Prologue directives should be at the first statement in the target statements array"); let foundUseStrict = false; let statementOffset = 0; diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 897a4a57502..83ed1df164a 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -515,7 +515,7 @@ namespace ts { const ancestorFacts = enterSubtree(HierarchyFacts.SourceFileExcludes, HierarchyFacts.SourceFileIncludes); const statements: Statement[] = []; startLexicalEnvironment(); - let statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ false); + let statementOffset = addStandardPrologue(statements, node.statements, /*ensureUseStrict*/ false); addCaptureThisForNodeIfNeeded(statements, node); statementOffset = addCustomPrologue(statements, node.statements, statementOffset, visitor); addRange(statements, visitNodes(node.statements, visitor, isStatement, statementOffset)); @@ -925,7 +925,7 @@ namespace ts { statementOffset = 0; } else if (constructor) { - statementOffset = addPrologueDirectives(statements, constructor.body.statements, /*ensureUseStrict*/ false); + statementOffset = addStandardPrologue(statements, constructor.body.statements, /*ensureUseStrict*/ false); } if (constructor) { @@ -1825,8 +1825,8 @@ namespace ts { resumeLexicalEnvironment(); if (isBlock(body)) { // ensureUseStrict is false because no new prologue-directive should be added. - // addPrologueDirectives will put already-existing directives at the beginning of the target statement-array - statementOffset = addPrologueDirectives(statements, body.statements, /*ensureUseStrict*/ false); + // addStandardPrologue will put already-existing directives at the beginning of the target statement-array + statementOffset = addStandardPrologue(statements, body.statements, /*ensureUseStrict*/ false); } addCaptureThisForNodeIfNeeded(statements, node);