From 324a0b408a08ec9f8680fe418dbcd7ebeb62ce3a Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sun, 4 Jan 2015 07:38:36 +1100 Subject: [PATCH] add optional protoToAssign transformer --- lib/6to5/file.js | 8 ++- lib/6to5/transformation/templates/defaults.js | 8 +++ lib/6to5/transformation/transform.js | 3 +- lib/6to5/transformation/transformer.js | 7 +-- .../transformers/_declarations.js | 4 ++ .../transformers/optional-proto-to-assign.js | 63 +++++++++++++++++++ .../assignment-expression/actual.js | 5 ++ .../assignment-expression/expected.js | 18 ++++++ .../assignment-statement/actual.js | 1 + .../assignment-statement/expected.js | 13 ++++ .../optional-proto-to-assign/class/actual.js | 3 + .../class/expected.js | 36 +++++++++++ .../object-literal/actual.js | 13 ++++ .../object-literal/expected.js | 10 +++ .../optional-proto-to-assign/options.json | 3 + 15 files changed, 189 insertions(+), 6 deletions(-) create mode 100644 lib/6to5/transformation/templates/defaults.js create mode 100644 lib/6to5/transformation/transformers/optional-proto-to-assign.js create mode 100644 test/fixtures/transformation/optional-proto-to-assign/assignment-expression/actual.js create mode 100644 test/fixtures/transformation/optional-proto-to-assign/assignment-expression/expected.js create mode 100644 test/fixtures/transformation/optional-proto-to-assign/assignment-statement/actual.js create mode 100644 test/fixtures/transformation/optional-proto-to-assign/assignment-statement/expected.js create mode 100644 test/fixtures/transformation/optional-proto-to-assign/class/actual.js create mode 100644 test/fixtures/transformation/optional-proto-to-assign/class/expected.js create mode 100644 test/fixtures/transformation/optional-proto-to-assign/object-literal/actual.js create mode 100644 test/fixtures/transformation/optional-proto-to-assign/object-literal/expected.js create mode 100644 test/fixtures/transformation/optional-proto-to-assign/options.json diff --git a/lib/6to5/file.js b/lib/6to5/file.js index a822d8eaa9..e7c2badea5 100644 --- a/lib/6to5/file.js +++ b/lib/6to5/file.js @@ -19,6 +19,7 @@ function File(opts) { File.declarations = [ "inherits", + "defaults", "prototype-properties", "apply-constructor", "tagged-template-literal", @@ -97,18 +98,23 @@ File.normaliseOptions = function (opts) { File.prototype.getTransformers = function () { var file = this; var transformers = []; + var secondPassTransformers = []; _.each(transform.transformers, function (transformer) { if (transformer.canRun(file)) { transformers.push(transformer); + if (transformer.secondPass) { + secondPassTransformers.push(transformer); + } + if (transformer.manipulateOptions) { transformer.manipulateOptions(file.opts, file); } } }); - return transformers; + return transformers.concat(secondPassTransformers); }; File.prototype.toArray = function (node, i) { diff --git a/lib/6to5/transformation/templates/defaults.js b/lib/6to5/transformation/templates/defaults.js new file mode 100644 index 0000000000..3fda0b6fb6 --- /dev/null +++ b/lib/6to5/transformation/templates/defaults.js @@ -0,0 +1,8 @@ +(function (obj, defaults) { + for (var key in defaults) { + if (obj[key] === undefined) { + obj[key] = defaults[key]; + } + } + return obj; +}) diff --git a/lib/6to5/transformation/transform.js b/lib/6to5/transformation/transform.js index 562c33f4b9..e78a0ea3b1 100644 --- a/lib/6to5/transformation/transform.js +++ b/lib/6to5/transformation/transform.js @@ -81,6 +81,8 @@ _.each({ generators: require("./transformers/es6-generators"), restParameters: require("./transformers/es6-rest-parameters"), + protoToAssign: require("./transformers/optional-proto-to-assign"), + _declarations: require("./transformers/_declarations"), // wrap up @@ -88,7 +90,6 @@ _.each({ _moduleFormatter: require("./transformers/_module-formatter"), useStrict: require("./transformers/use-strict"), - protoToAssign: require("./transformers/optional-proto-to-assign"), coreAliasing: require("./transformers/optional-core-aliasing"), undefinedToVoid: require("./transformers/optional-undefined-to-void"), diff --git a/lib/6to5/transformation/transformer.js b/lib/6to5/transformation/transformer.js index c0f8049195..7239e5e976 100644 --- a/lib/6to5/transformation/transformer.js +++ b/lib/6to5/transformation/transformer.js @@ -7,6 +7,7 @@ var _ = require("lodash"); function Transformer(key, transformer, opts) { this.manipulateOptions = transformer.manipulateOptions; this.experimental = !!transformer.experimental; + this.secondPass = !!transformer.secondPass; this.transformer = Transformer.normalise(transformer); this.optional = !!transformer.optional; this.opts = opts || {}; @@ -41,16 +42,14 @@ Transformer.normalise = function (transformer) { Transformer.prototype.astRun = function (file, key) { var transformer = this.transformer; - var ast = file.ast; if (transformer.ast && transformer.ast[key]) { - transformer.ast[key](ast, file); + transformer.ast[key](file.ast, file); } }; Transformer.prototype.transform = function (file) { var transformer = this.transformer; - var ast = file.ast; var build = function (exit) { return function (node, parent, scope) { @@ -67,7 +66,7 @@ Transformer.prototype.transform = function (file) { this.astRun(file, "before"); - traverse(ast, { + traverse(file.ast, { enter: build(), exit: build(true) }); diff --git a/lib/6to5/transformation/transformers/_declarations.js b/lib/6to5/transformation/transformers/_declarations.js index a5eb107e41..ae0f355b08 100644 --- a/lib/6to5/transformation/transformers/_declarations.js +++ b/lib/6to5/transformation/transformers/_declarations.js @@ -1,5 +1,7 @@ var t = require("../../types"); +exports.secondPass = true; + exports.BlockStatement = exports.Program = function (node) { var kinds = {}; @@ -22,4 +24,6 @@ exports.Program = function (node) { for (kind in kinds) { node.body.unshift(t.variableDeclaration(kind, kinds[kind])); } + + node._declarations = null; }; diff --git a/lib/6to5/transformation/transformers/optional-proto-to-assign.js b/lib/6to5/transformation/transformers/optional-proto-to-assign.js new file mode 100644 index 0000000000..3101abf474 --- /dev/null +++ b/lib/6to5/transformation/transformers/optional-proto-to-assign.js @@ -0,0 +1,63 @@ +var t = require("../../types"); +var _ = require("lodash"); + +var OBJECT_ASSIGN_MEMBER = t.memberExpression(t.identifier("Object"), t.identifier("assign")); + +var isProtoKey = function (node) { + return t.isLiteral(t.toComputedKey(node, node.key), { value: "__proto__" }); +}; + +var isProtoAssignmentExpression = function (node) { + var left = node.left; + return t.isMemberExpression(left) && t.isLiteral(t.toComputedKey(left, left.property), { value: "__proto__" }); +}; + +var buildDefaultsCallExpression = function (expr, ref, file) { + return t.expressionStatement(t.callExpression(file.addDeclaration("defaults"), [ref, expr.right])); +}; + +exports.optional = true; +exports.secondPass = true; + +exports.AssignmentExpression = function (node, parent, file, scope) { + if (t.isExpressionStatement(parent)) return; + if (!isProtoAssignmentExpression(node)) return; + + var nodes = []; + var left = node.left.object; + var temp = scope.generateTempBasedOnNode(node.left.object, file); + + nodes.push(t.expressionStatement(t.assignmentExpression("=", temp, left))); + nodes.push(buildDefaultsCallExpression(node, temp, file)); + if (temp) nodes.push(temp); + + return t.toSequenceExpression(nodes); +}; + +exports.ExpressionStatement = function (node, parent, file, scope) { + var expr = node.expression; + if (!t.isAssignmentExpression(expr, { operator: "=" })) return; + + if (isProtoAssignmentExpression(expr)) { + return buildDefaultsCallExpression(expr, expr.left.object, file); + } +}; + +exports.ObjectExpression = function (node) { + var proto; + + for (var i in node.properties) { + var prop = node.properties[i]; + + if (isProtoKey(prop)) { + proto = prop.value; + _.pull(node.properties, prop); + } + } + + if (proto) { + var args = [t.objectExpression([]), proto]; + if (node.properties.length) args.push(node); + return t.callExpression(OBJECT_ASSIGN_MEMBER, args); + } +}; diff --git a/test/fixtures/transformation/optional-proto-to-assign/assignment-expression/actual.js b/test/fixtures/transformation/optional-proto-to-assign/assignment-expression/actual.js new file mode 100644 index 0000000000..25b5b0e4c5 --- /dev/null +++ b/test/fixtures/transformation/optional-proto-to-assign/assignment-expression/actual.js @@ -0,0 +1,5 @@ +console.log(foo.__proto__ = bar); + +console.log(foo[bar].__proto__ = bar); + +console.log(foo[bar()].__proto__ = bar); diff --git a/test/fixtures/transformation/optional-proto-to-assign/assignment-expression/expected.js b/test/fixtures/transformation/optional-proto-to-assign/assignment-expression/expected.js new file mode 100644 index 0000000000..6a66b4e972 --- /dev/null +++ b/test/fixtures/transformation/optional-proto-to-assign/assignment-expression/expected.js @@ -0,0 +1,18 @@ +"use strict"; + +var _foo, _foo$bar, _foo2; +var _defaults = function (obj, defaults) { + for (var key in defaults) { + if (obj[key] === undefined) { + obj[key] = defaults[key]; + } + } + + return obj; +}; + +console.log((_foo = foo, _defaults(_foo, bar), _foo)); + +console.log((_foo$bar = foo[bar], _defaults(_foo$bar, bar), _foo$bar)); + +console.log((_foo2 = foo[bar()], _defaults(_foo2, bar), _foo2)); diff --git a/test/fixtures/transformation/optional-proto-to-assign/assignment-statement/actual.js b/test/fixtures/transformation/optional-proto-to-assign/assignment-statement/actual.js new file mode 100644 index 0000000000..3a79c6d13a --- /dev/null +++ b/test/fixtures/transformation/optional-proto-to-assign/assignment-statement/actual.js @@ -0,0 +1 @@ +obj.__proto__ = bar; diff --git a/test/fixtures/transformation/optional-proto-to-assign/assignment-statement/expected.js b/test/fixtures/transformation/optional-proto-to-assign/assignment-statement/expected.js new file mode 100644 index 0000000000..10b661e74f --- /dev/null +++ b/test/fixtures/transformation/optional-proto-to-assign/assignment-statement/expected.js @@ -0,0 +1,13 @@ +"use strict"; + +var _defaults = function (obj, defaults) { + for (var key in defaults) { + if (obj[key] === undefined) { + obj[key] = defaults[key]; + } + } + + return obj; +}; + +_defaults(obj, bar); diff --git a/test/fixtures/transformation/optional-proto-to-assign/class/actual.js b/test/fixtures/transformation/optional-proto-to-assign/class/actual.js new file mode 100644 index 0000000000..9b78abd703 --- /dev/null +++ b/test/fixtures/transformation/optional-proto-to-assign/class/actual.js @@ -0,0 +1,3 @@ +class Foo extends Bar { + +} diff --git a/test/fixtures/transformation/optional-proto-to-assign/class/expected.js b/test/fixtures/transformation/optional-proto-to-assign/class/expected.js new file mode 100644 index 0000000000..2f4501d427 --- /dev/null +++ b/test/fixtures/transformation/optional-proto-to-assign/class/expected.js @@ -0,0 +1,36 @@ +"use strict"; + +var _defaults = function (obj, defaults) { + for (var key in defaults) { + if (obj[key] === undefined) { + obj[key] = defaults[key]; + } + } + + return obj; +}; + +var _inherits = function (child, parent) { + child.prototype = Object.create(parent && parent.prototype, { + constructor: { + value: child, + enumerable: false, + writable: true, + configurable: true + } + }); + if (parent) _defaults(child, parent); +}; + +var Foo = (function () { + var _Bar = Bar; + var Foo = function Foo() { + if (_Bar) { + _Bar.apply(this, arguments); + } + }; + + _inherits(Foo, _Bar); + + return Foo; +})(); diff --git a/test/fixtures/transformation/optional-proto-to-assign/object-literal/actual.js b/test/fixtures/transformation/optional-proto-to-assign/object-literal/actual.js new file mode 100644 index 0000000000..63cf63406e --- /dev/null +++ b/test/fixtures/transformation/optional-proto-to-assign/object-literal/actual.js @@ -0,0 +1,13 @@ +var foo = { + __proto__: bar +}; + +var foo = { + bar: "foo", + __proto__: bar +}; + +var foo = { + __proto__: bar, + bar: "foo" +}; diff --git a/test/fixtures/transformation/optional-proto-to-assign/object-literal/expected.js b/test/fixtures/transformation/optional-proto-to-assign/object-literal/expected.js new file mode 100644 index 0000000000..31b92ba185 --- /dev/null +++ b/test/fixtures/transformation/optional-proto-to-assign/object-literal/expected.js @@ -0,0 +1,10 @@ +"use strict"; + +var foo = Object.assign({}, bar); + +var foo = Object.assign({}, bar, { + bar: "foo" }); + +var foo = Object.assign({}, bar, { + bar: "foo" +}); diff --git a/test/fixtures/transformation/optional-proto-to-assign/options.json b/test/fixtures/transformation/optional-proto-to-assign/options.json new file mode 100644 index 0000000000..bff9598732 --- /dev/null +++ b/test/fixtures/transformation/optional-proto-to-assign/options.json @@ -0,0 +1,3 @@ +{ + "optional": ["protoToAssign"] +}