From eed750206c24ed8dc2c5c934abc9d7ec4a728c57 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Tue, 30 Jun 2015 01:22:06 -0500 Subject: [PATCH 1/3] Convert template objects to singletons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create file-scoped template objects that are instantiated once and reused between different tagged template evaluations. This is a closer match to the spec behavior which demands reuse, but does not match the spec exactly with respect to scoping as it’s possible to have similar template objects defined in different file scopes within the same program. For now we are not able to cleanly handle this case as it would require a registry or similar lookup mechanism that is consistent across different combinations of builds and optimizers. This has a 40x increase in six-speed throughput. Partial fix for #971 --- .../babel/src/transformation/file/index.js | 24 +++++++++++++++++++ .../transformers/es6/template-literals.js | 4 +++- packages/babel/src/traversal/scope/index.js | 5 ++-- .../es6.template-literals/tag-loose/actual.js | 2 ++ .../tag-loose/expected.js | 4 +++- .../es6.template-literals/tag/expected.js | 8 ++++++- 6 files changed, 42 insertions(+), 5 deletions(-) diff --git a/packages/babel/src/transformation/file/index.js b/packages/babel/src/transformation/file/index.js index 6505704cb3..7a81feb583 100644 --- a/packages/babel/src/transformation/file/index.js +++ b/packages/babel/src/transformation/file/index.js @@ -407,6 +407,30 @@ export default class File { return uid; } + addTemplateObject(helperName: string, strings: Array, raw: Array): Object { + // Generate a unique name based on the string literals so we dedupe + // identical strings used in the program. + var stringIds = raw.elements.map(function(string) { + return string.value; + }); + var name = `${helperName}_${raw.elements.length}_${stringIds.join(",")}`; + + var declar = this.declarations[name]; + if (declar) return declar; + + var uid = this.declarations[name] = this.scope.generateUidIdentifier(name); + + var helperId = this.addHelper(helperName); + var init = t.callExpression(helperId, [strings, raw]); + init._compact = true; + this.scope.push({ + id: uid, + init: init, + _blockHoist: 1.9 // This ensures that we don't fail if not using function expression helpers + }); + return uid; + } + /** * [Please add a description.] */ diff --git a/packages/babel/src/transformation/transformers/es6/template-literals.js b/packages/babel/src/transformation/transformers/es6/template-literals.js index 83f35c5e89..b561f64757 100644 --- a/packages/babel/src/transformation/transformers/es6/template-literals.js +++ b/packages/babel/src/transformation/transformers/es6/template-literals.js @@ -64,7 +64,9 @@ export var visitor = { var templateName = "tagged-template-literal"; if (file.isLoose("es6.templateLiterals")) templateName += "-loose"; - args.push(t.callExpression(file.addHelper(templateName), [strings, raw])); + + var templateObject = file.addTemplateObject(templateName, strings, raw); + args.push(templateObject); args = args.concat(quasi.expressions); diff --git a/packages/babel/src/traversal/scope/index.js b/packages/babel/src/traversal/scope/index.js index 7ef81af584..e588276c68 100644 --- a/packages/babel/src/traversal/scope/index.js +++ b/packages/babel/src/traversal/scope/index.js @@ -829,14 +829,15 @@ export default class Scope { var unique = opts.unique; var kind = opts.kind || "var"; + var blockHoist = opts._blockHoist || 2; - var dataKey = `declaration:${kind}`; + var dataKey = `declaration:${kind}:${blockHoist}`; var declar = !unique && path.getData(dataKey); if (!declar) { declar = t.variableDeclaration(kind, []); declar._generated = true; - declar._blockHoist = 2; + declar._blockHoist = blockHoist; this.hub.file.attachAuxiliaryComment(declar); diff --git a/packages/babel/test/fixtures/transformation/es6.template-literals/tag-loose/actual.js b/packages/babel/test/fixtures/transformation/es6.template-literals/tag-loose/actual.js index 7fe1864736..cfa5638dbd 100644 --- a/packages/babel/test/fixtures/transformation/es6.template-literals/tag-loose/actual.js +++ b/packages/babel/test/fixtures/transformation/es6.template-literals/tag-loose/actual.js @@ -1 +1,3 @@ var foo = bar`wow\na${ 42 }b ${_.foobar()}`; +var bar = bar`wow\nab${ 42 } ${_.foobar()}`; +var bar = bar`wow\naB${ 42 } ${_.foobar()}`; diff --git a/packages/babel/test/fixtures/transformation/es6.template-literals/tag-loose/expected.js b/packages/babel/test/fixtures/transformation/es6.template-literals/tag-loose/expected.js index a2264017ba..3665e56ad2 100644 --- a/packages/babel/test/fixtures/transformation/es6.template-literals/tag-loose/expected.js +++ b/packages/babel/test/fixtures/transformation/es6.template-literals/tag-loose/expected.js @@ -1,5 +1,7 @@ "use strict"; +var _taggedTemplateLiteralLoose_3_wowNaB = _taggedTemplateLiteralLoose(["wow\na", "b ", ""], ["wow\\na", "b ", ""]); + function _taggedTemplateLiteralLoose(strings, raw) { strings.raw = raw; return strings; } -var foo = bar(_taggedTemplateLiteralLoose(["wow\na", "b ", ""], ["wow\\na", "b ", ""]), 42, _.foobar()); \ No newline at end of file +var foo = bar(_taggedTemplateLiteralLoose_3_wowNaB, 42, _.foobar()); \ No newline at end of file diff --git a/packages/babel/test/fixtures/transformation/es6.template-literals/tag/expected.js b/packages/babel/test/fixtures/transformation/es6.template-literals/tag/expected.js index db7bc65355..1a4289dd50 100644 --- a/packages/babel/test/fixtures/transformation/es6.template-literals/tag/expected.js +++ b/packages/babel/test/fixtures/transformation/es6.template-literals/tag/expected.js @@ -1,5 +1,11 @@ "use strict"; +var _taggedTemplateLiteral_3_wowNaB = _taggedTemplateLiteral(["wow\na", "b ", ""], ["wow\\na", "b ", ""]), + _taggedTemplateLiteral_3_wowNab = _taggedTemplateLiteral(["wow\nab", " ", ""], ["wow\\nab", " ", ""]), + _taggedTemplateLiteral_3_wowNaB2 = _taggedTemplateLiteral(["wow\naB", " ", ""], ["wow\\naB", " ", ""]); + function _taggedTemplateLiteral(strings, raw) { return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); } -var foo = bar(_taggedTemplateLiteral(["wow\na", "b ", ""], ["wow\\na", "b ", ""]), 42, _.foobar()); \ No newline at end of file +var foo = bar(_taggedTemplateLiteral_3_wowNaB, 42, _.foobar()); +var bar = bar(_taggedTemplateLiteral_3_wowNab, 42, _.foobar()); +var bar = bar(_taggedTemplateLiteral_3_wowNaB2, 42, _.foobar()); \ No newline at end of file From b2e3a73e2ff406778a65415747391df6bf85a2ce Mon Sep 17 00:00:00 2001 From: kpdecker Date: Sun, 19 Jul 2015 10:39:38 +0300 Subject: [PATCH 2/3] Use generic name for templateObject Rather than using variable names that could be quite long, just use indexed generic names that use runtime lookup for the string parameter mapping. --- packages/babel/src/transformation/file/index.js | 2 +- .../es6.template-literals/tag-loose/actual.js | 2 +- .../es6.template-literals/tag-loose/expected.js | 8 ++++++-- .../transformation/es6.template-literals/tag/expected.js | 8 ++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/babel/src/transformation/file/index.js b/packages/babel/src/transformation/file/index.js index 7a81feb583..1253224892 100644 --- a/packages/babel/src/transformation/file/index.js +++ b/packages/babel/src/transformation/file/index.js @@ -418,7 +418,7 @@ export default class File { var declar = this.declarations[name]; if (declar) return declar; - var uid = this.declarations[name] = this.scope.generateUidIdentifier(name); + var uid = this.declarations[name] = this.scope.generateUidIdentifier("templateObject"); var helperId = this.addHelper(helperName); var init = t.callExpression(helperId, [strings, raw]); diff --git a/packages/babel/test/fixtures/transformation/es6.template-literals/tag-loose/actual.js b/packages/babel/test/fixtures/transformation/es6.template-literals/tag-loose/actual.js index cfa5638dbd..52419bb505 100644 --- a/packages/babel/test/fixtures/transformation/es6.template-literals/tag-loose/actual.js +++ b/packages/babel/test/fixtures/transformation/es6.template-literals/tag-loose/actual.js @@ -1,3 +1,3 @@ var foo = bar`wow\na${ 42 }b ${_.foobar()}`; var bar = bar`wow\nab${ 42 } ${_.foobar()}`; -var bar = bar`wow\naB${ 42 } ${_.foobar()}`; +var bar = bar`wow\naB${ 42 } ${_.baz()}`; diff --git a/packages/babel/test/fixtures/transformation/es6.template-literals/tag-loose/expected.js b/packages/babel/test/fixtures/transformation/es6.template-literals/tag-loose/expected.js index 3665e56ad2..9378680632 100644 --- a/packages/babel/test/fixtures/transformation/es6.template-literals/tag-loose/expected.js +++ b/packages/babel/test/fixtures/transformation/es6.template-literals/tag-loose/expected.js @@ -1,7 +1,11 @@ "use strict"; -var _taggedTemplateLiteralLoose_3_wowNaB = _taggedTemplateLiteralLoose(["wow\na", "b ", ""], ["wow\\na", "b ", ""]); +var _templateObject = _taggedTemplateLiteralLoose(["wow\na", "b ", ""], ["wow\\na", "b ", ""]), + _templateObject2 = _taggedTemplateLiteralLoose(["wow\nab", " ", ""], ["wow\\nab", " ", ""]), + _templateObject3 = _taggedTemplateLiteralLoose(["wow\naB", " ", ""], ["wow\\naB", " ", ""]); function _taggedTemplateLiteralLoose(strings, raw) { strings.raw = raw; return strings; } -var foo = bar(_taggedTemplateLiteralLoose_3_wowNaB, 42, _.foobar()); \ No newline at end of file +var foo = bar(_templateObject, 42, _.foobar()); +var bar = bar(_templateObject2, 42, _.foobar()); +var bar = bar(_templateObject3, 42, _.baz()); \ No newline at end of file diff --git a/packages/babel/test/fixtures/transformation/es6.template-literals/tag/expected.js b/packages/babel/test/fixtures/transformation/es6.template-literals/tag/expected.js index 1a4289dd50..7749db566b 100644 --- a/packages/babel/test/fixtures/transformation/es6.template-literals/tag/expected.js +++ b/packages/babel/test/fixtures/transformation/es6.template-literals/tag/expected.js @@ -1,11 +1,7 @@ "use strict"; -var _taggedTemplateLiteral_3_wowNaB = _taggedTemplateLiteral(["wow\na", "b ", ""], ["wow\\na", "b ", ""]), - _taggedTemplateLiteral_3_wowNab = _taggedTemplateLiteral(["wow\nab", " ", ""], ["wow\\nab", " ", ""]), - _taggedTemplateLiteral_3_wowNaB2 = _taggedTemplateLiteral(["wow\naB", " ", ""], ["wow\\naB", " ", ""]); +var _templateObject = _taggedTemplateLiteral(["wow\na", "b ", ""], ["wow\\na", "b ", ""]); function _taggedTemplateLiteral(strings, raw) { return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); } -var foo = bar(_taggedTemplateLiteral_3_wowNaB, 42, _.foobar()); -var bar = bar(_taggedTemplateLiteral_3_wowNab, 42, _.foobar()); -var bar = bar(_taggedTemplateLiteral_3_wowNaB2, 42, _.foobar()); \ No newline at end of file +var foo = bar(_templateObject, 42, _.foobar()); From 4eb1a45f0218bbd7d9e08f6397ac6a41e5d2731c Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 20 Jul 2015 11:06:09 +0300 Subject: [PATCH 3/3] Handle _blockHoist value of zero safely --- packages/babel/src/traversal/scope/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel/src/traversal/scope/index.js b/packages/babel/src/traversal/scope/index.js index e588276c68..0d577152b4 100644 --- a/packages/babel/src/traversal/scope/index.js +++ b/packages/babel/src/traversal/scope/index.js @@ -829,7 +829,7 @@ export default class Scope { var unique = opts.unique; var kind = opts.kind || "var"; - var blockHoist = opts._blockHoist || 2; + var blockHoist = opts._blockHoist == null ? 2 : opts._blockHoist; var dataKey = `declaration:${kind}:${blockHoist}`; var declar = !unique && path.getData(dataKey);