From e9f984d3cedc689f89c33bcf5c2524ab3055b0cd Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 7 Apr 2016 18:30:26 -0700 Subject: [PATCH 1/4] Use an emit helper for JSX Spread Attributes. --- src/compiler/binder.ts | 9 +++++++++ src/compiler/emitter.ts | 23 +++++++++++++++++++---- src/compiler/types.ts | 6 +++++- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 98bc54e88c3..642eb28ae3d 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -122,6 +122,7 @@ namespace ts { let hasAsyncFunctions: boolean; let hasDecorators: boolean; let hasParameterDecorators: boolean; + let hasJsxSpreadAttribute: boolean; // If this file is an external module, then it is automatically in strict-mode according to // ES6. If it is not an external module, then we'll determine if it is in strict mode or @@ -161,6 +162,7 @@ namespace ts { hasAsyncFunctions = false; hasDecorators = false; hasParameterDecorators = false; + hasJsxSpreadAttribute = false; } return bindSourceFile; @@ -498,6 +500,9 @@ namespace ts { if (hasAsyncFunctions) { flags |= NodeFlags.HasAsyncFunctions; } + if (hasJsxSpreadAttribute) { + flags |= NodeFlags.HasJsxSpreadAttribute; + } } node.flags = flags; @@ -1289,6 +1294,10 @@ namespace ts { case SyntaxKind.EnumMember: return bindPropertyOrMethodOrAccessor(node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes); + case SyntaxKind.JsxSpreadAttribute: + hasJsxSpreadAttribute = true; + return; + case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 212d9f05510..1d72dcdb683 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -345,6 +345,15 @@ var __extends = (this && this.__extends) || function (d, b) { d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); };`; + const assignHelper = ` +var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var i = 1, n = arguments.length; i < n; i++) { + var s = arguments[i]; + if (s != null) for (var p in s) if (s.hasOwnProperty(p)) t[p] = s[p]; + } + return t; +};`; + // emit output for the __decorate helper function const decorateHelper = ` var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { @@ -540,6 +549,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge let convertedLoopState: ConvertedLoopState; let extendsEmitted: boolean; + let assignEmitted: boolean; let decorateEmitted: boolean; let paramEmitted: boolean; let awaiterEmitted: boolean; @@ -623,6 +633,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge decorateEmitted = false; paramEmitted = false; awaiterEmitted = false; + assignEmitted = false; tempFlags = 0; tempVariables = undefined; tempParameters = undefined; @@ -1259,11 +1270,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge } else { // Either emit one big object literal (no spread attribs), or - // a call to React.__spread + // a call to the __assign helper const attrs = openingNode.attributes; if (forEach(attrs, attr => attr.kind === SyntaxKind.JsxSpreadAttribute)) { - emitExpressionIdentifier(syntheticReactRef); - write(".__spread("); + write("__assign("); let haveOpenedObjectLiteral = false; for (let i = 0; i < attrs.length; i++) { @@ -7610,11 +7620,16 @@ const _super = (function (geti, seti) { if (!compilerOptions.noEmitHelpers) { // Only Emit __extends function when target ES5. // For target ES6 and above, we can emit classDeclaration as is. - if ((languageVersion < ScriptTarget.ES6) && (!extendsEmitted && node.flags & NodeFlags.HasClassExtends)) { + if (languageVersion < ScriptTarget.ES6 && !extendsEmitted && node.flags & NodeFlags.HasClassExtends) { writeLines(extendsHelper); extendsEmitted = true; } + if (compilerOptions.jsx !== JsxEmit.Preserve && !assignEmitted && (node.flags & NodeFlags.HasJsxSpreadAttribute)) { + writeLines(assignHelper); + assignEmitted = true; + } + if (!decorateEmitted && node.flags & NodeFlags.HasDecorators) { writeLines(decorateHelper); if (compilerOptions.emitDecoratorMetadata) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 2692354258f..374f2c9ff62 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -369,7 +369,7 @@ namespace ts { FirstNode = QualifiedName, } - export const enum NodeFlags { + export const enum NodeFlags { None = 0, Export = 1 << 1, // Declarations Ambient = 1 << 2, // Declarations @@ -397,6 +397,10 @@ namespace ts { HasParamDecorators = 1 << 24, // If the file has parameter decorators (initialized by binding) HasAsyncFunctions = 1 << 25, // If the file has async functions (initialized by binding) + // This was picked out from the 'master' branch. + // To keep the flags consistent, we're skipping a few ahead. + HasJsxSpreadAttribute = 1 << 30, + Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async, AccessibilityModifier = Public | Private | Protected, BlockScoped = Let | Const, From 6aa9adab72547d589917f8091176ec1b14692d83 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 7 Apr 2016 23:25:32 -0700 Subject: [PATCH 2/4] Remove unnecessary 'null'/'undefined' check, removed temp for args length. --- src/compiler/emitter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 1d72dcdb683..a2ad2431c98 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -347,9 +347,9 @@ var __extends = (this && this.__extends) || function (d, b) { const assignHelper = ` var __assign = (this && this.__assign) || Object.assign || function(t) { - for (var i = 1, n = arguments.length; i < n; i++) { + for (var i = 1; i < arguments.length; i++) { var s = arguments[i]; - if (s != null) for (var p in s) if (s.hasOwnProperty(p)) t[p] = s[p]; + for (var p in s) if (s.hasOwnProperty(p)) t[p] = s[p]; } return t; };`; From 50d570b12c1a19ab01c19d71c8dd39e5bdf0a2d3 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Fri, 8 Apr 2016 12:34:25 -0700 Subject: [PATCH 3/4] Changed emit to use 'Object.prototype.hasOwnProperty'. --- src/compiler/emitter.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index a2ad2431c98..9275a586445 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -347,9 +347,10 @@ var __extends = (this && this.__extends) || function (d, b) { const assignHelper = ` var __assign = (this && this.__assign) || Object.assign || function(t) { - for (var i = 1; i < arguments.length; i++) { - var s = arguments[i]; - for (var p in s) if (s.hasOwnProperty(p)) t[p] = s[p]; + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; } return t; };`; From 662dc64a882e140f7d8ccb8d5e057302d4ec14bd Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Fri, 8 Apr 2016 13:39:33 -0700 Subject: [PATCH 4/4] Accepted baselines. --- .../reference/reactNamespaceJSXEmit.js | 12 ++++++++++-- .../reference/tsxExternalModuleEmit2.js | 10 +++++++++- tests/baselines/reference/tsxReactEmit2.js | 18 +++++++++++++----- tests/baselines/reference/tsxReactEmit4.js | 10 +++++++++- tests/baselines/reference/tsxReactEmit5.js | 10 +++++++++- tests/baselines/reference/tsxReactEmit6.js | 10 +++++++++- 6 files changed, 59 insertions(+), 11 deletions(-) diff --git a/tests/baselines/reference/reactNamespaceJSXEmit.js b/tests/baselines/reference/reactNamespaceJSXEmit.js index 26967d56b7b..ced18ebc031 100644 --- a/tests/baselines/reference/reactNamespaceJSXEmit.js +++ b/tests/baselines/reference/reactNamespaceJSXEmit.js @@ -13,8 +13,16 @@ declare var x: any; //// [reactNamespaceJSXEmit.js] +var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; myReactLib.createElement("foo", {data: true}); myReactLib.createElement(Bar, {x: x}); myReactLib.createElement("x-component", null); -myReactLib.createElement(Bar, myReactLib.__spread({}, x)); -myReactLib.createElement(Bar, myReactLib.__spread({}, x, {y: 2})); +myReactLib.createElement(Bar, __assign({}, x)); +myReactLib.createElement(Bar, __assign({}, x, {y: 2})); diff --git a/tests/baselines/reference/tsxExternalModuleEmit2.js b/tests/baselines/reference/tsxExternalModuleEmit2.js index 2233c5181cb..25e186e140f 100644 --- a/tests/baselines/reference/tsxExternalModuleEmit2.js +++ b/tests/baselines/reference/tsxExternalModuleEmit2.js @@ -19,8 +19,16 @@ declare var Foo, React; //// [app.js] "use strict"; +var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; var mod_1 = require('mod'); // Should see mod_1['default'] in emit here React.createElement(Foo, {handler: mod_1["default"]}); // Should see mod_1['default'] in emit here -React.createElement(Foo, React.__spread({}, mod_1["default"])); +React.createElement(Foo, __assign({}, mod_1["default"])); diff --git a/tests/baselines/reference/tsxReactEmit2.js b/tests/baselines/reference/tsxReactEmit2.js index fd7fff16976..051c7380171 100644 --- a/tests/baselines/reference/tsxReactEmit2.js +++ b/tests/baselines/reference/tsxReactEmit2.js @@ -16,9 +16,17 @@ var spreads5 =
{p2}
; //// [file.js] +var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; var p1, p2, p3; -var spreads1 = React.createElement("div", React.__spread({}, p1), p2); -var spreads2 = React.createElement("div", React.__spread({}, p1), p2); -var spreads3 = React.createElement("div", React.__spread({x: p3}, p1), p2); -var spreads4 = React.createElement("div", React.__spread({}, p1, {x: p3}), p2); -var spreads5 = React.createElement("div", React.__spread({x: p2}, p1, {y: p3}), p2); +var spreads1 = React.createElement("div", __assign({}, p1), p2); +var spreads2 = React.createElement("div", __assign({}, p1), p2); +var spreads3 = React.createElement("div", __assign({x: p3}, p1), p2); +var spreads4 = React.createElement("div", __assign({}, p1, {x: p3}), p2); +var spreads5 = React.createElement("div", __assign({x: p2}, p1, {y: p3}), p2); diff --git a/tests/baselines/reference/tsxReactEmit4.js b/tests/baselines/reference/tsxReactEmit4.js index d61cea24d2c..3b554f17cb7 100644 --- a/tests/baselines/reference/tsxReactEmit4.js +++ b/tests/baselines/reference/tsxReactEmit4.js @@ -18,7 +18,15 @@ var openClosed1 =
var spread1 =
; //// [file.js] +var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; var p; var openClosed1 = React.createElement("div", null, blah); // Should emit React.__spread({}, p, {x: 0}) -var spread1 = React.createElement("div", React.__spread({}, p, {x: 0})); +var spread1 = React.createElement("div", __assign({}, p, {x: 0})); diff --git a/tests/baselines/reference/tsxReactEmit5.js b/tests/baselines/reference/tsxReactEmit5.js index 06b05f67d63..a6dde25edc3 100644 --- a/tests/baselines/reference/tsxReactEmit5.js +++ b/tests/baselines/reference/tsxReactEmit5.js @@ -23,8 +23,16 @@ var spread1 =
; //// [file.js] //// [react-consumer.js] "use strict"; +var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; var test_1 = require("./test"); // Should emit test_1.React.createElement // and React.__spread var foo; -var spread1 = test_1.React.createElement("div", test_1.React.__spread({x: ''}, foo, {y: ''})); +var spread1 = test_1.React.createElement("div", __assign({x: ''}, foo, {y: ''})); diff --git a/tests/baselines/reference/tsxReactEmit6.js b/tests/baselines/reference/tsxReactEmit6.js index 4f583929004..cb41bf4d1eb 100644 --- a/tests/baselines/reference/tsxReactEmit6.js +++ b/tests/baselines/reference/tsxReactEmit6.js @@ -28,6 +28,14 @@ namespace M { //// [file.js] //// [react-consumer.js] +var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; var M; (function (M) { })(M || (M = {})); @@ -36,7 +44,7 @@ var M; // Should emit M.React.createElement // and M.React.__spread var foo; - var spread1 = M.React.createElement("div", M.React.__spread({x: ''}, foo, {y: ''})); + var spread1 = M.React.createElement("div", __assign({x: ''}, foo, {y: ''})); // Quotes var x = M.React.createElement("div", null, "This \"quote\" thing"); })(M || (M = {}));