diff --git a/lib/6to5/transformation/templates/class-super-constructor-call-fast.js b/lib/6to5/transformation/templates/class-super-constructor-call-fast.js new file mode 100644 index 0000000000..dceab5e75b --- /dev/null +++ b/lib/6to5/transformation/templates/class-super-constructor-call-fast.js @@ -0,0 +1,5 @@ +(function () { + if (SUPER_NAME != null) { + SUPER_NAME.apply(this, arguments); + } +}); diff --git a/lib/6to5/transformation/transform.js b/lib/6to5/transformation/transform.js index 7d156eafd8..affd18c69c 100644 --- a/lib/6to5/transformation/transform.js +++ b/lib/6to5/transformation/transform.js @@ -58,6 +58,7 @@ _.each({ arrayComprehension: require("./transformers/es7-array-comprehension"), generatorComprehension: require("./transformers/es7-generator-comprehension"), arrowFunctions: require("./transformers/es6-arrow-functions"), + classesFastSuper: require("./transformers/optional-classes-fast-super"), classes: require("./transformers/es6-classes"), objectSpread: require("./transformers/es7-object-spread"), diff --git a/lib/6to5/transformation/transformers/es6-classes.js b/lib/6to5/transformation/transformers/es6-classes.js index e1ccf2cf2f..fd824f86c7 100644 --- a/lib/6to5/transformation/transformers/es6-classes.js +++ b/lib/6to5/transformation/transformers/es6-classes.js @@ -291,7 +291,7 @@ Class.prototype.replaceInstanceSuperReferences = function (methodNode) { */ Class.prototype.pushConstructor = function (method) { - if (method.kind !== "") { + if (method.kind) { throw this.file.errorWithNode(method, "illegal kind for constructor method"); } diff --git a/lib/6to5/transformation/transformers/optional-classes-fast-super.js b/lib/6to5/transformation/transformers/optional-classes-fast-super.js new file mode 100644 index 0000000000..7f65c08c93 --- /dev/null +++ b/lib/6to5/transformation/transformers/optional-classes-fast-super.js @@ -0,0 +1,74 @@ +var traverse = require("../../traverse"); +var util = require("../../util"); +var t = require("../../types"); + +exports.optional = true; + +exports.Class = function (node) { + var superClass = node.superClass || t.identifier("Function"); + + var hasConstructor = false; + var body = node.body.body; + + for (var i in body) { + var methodNode = body[i]; + + hasConstructor = hasConstructor || methodNode.key.name === "constructor"; + + traverse(methodNode, { + enter: function (node, parent) { + if (t.isIdentifier(node, { name: "super" })) { + return superIdentifier(superClass, methodNode, node, parent); + } else if (t.isCallExpression(node)) { + var callee = node.callee; + if (!t.isMemberExpression(callee)) return; + if (callee.object.name !== "super") return; + + // super.test(); -> ClassName.prototype.MethodName.call(this); + t.appendToMemberExpression(callee, t.identifier("call")); + node.arguments.unshift(t.thisExpression()); + } + } + }); + } + + if (node.superClass && !hasConstructor) { + body.unshift(t.methodDefinition( + t.identifier("constructor"), + util.template("class-super-constructor-call-fast", { + SUPER_NAME: superClass + }) + )); + } +}; + +var superIdentifier = function (superClass, methodNode, id, parent) { + var methodName = methodNode.key; + + if (parent.property === id) { + return; + } else if (t.isCallExpression(parent, { callee: id })) { + // super(); -> ClassName.prototype.MethodName.call(this); + parent.arguments.unshift(t.thisExpression()); + + if (methodName.name === "constructor") { + // constructor() { super(); } + return t.memberExpression(superClass, t.identifier("call")); + } else { + id = superClass; + + // foo() { super(); } + if (!methodNode.static) { + id = t.memberExpression(id, t.identifier("prototype")); + } + + id = t.memberExpression(id, methodName, methodNode.computed); + return t.memberExpression(id, t.identifier("call")); + } + } else if (t.isMemberExpression(parent) && !methodNode.static) { + // super.test -> ClassName.prototype.test + return t.memberExpression(superClass, t.identifier("prototype")); + } else { + return superClass; + } +}; diff --git a/lib/6to5/types/builder-keys.json b/lib/6to5/types/builder-keys.json index e9ac7987be..f2217cc921 100644 --- a/lib/6to5/types/builder-keys.json +++ b/lib/6to5/types/builder-keys.json @@ -16,6 +16,7 @@ "Literal": ["value"], "LogicalExpression": ["operator", "left", "right"], "MemberExpression": ["object", "property", "computed"], + "MethodDefinition": ["key", "value", "computed", "kind"], "NewExpression": ["callee", "arguments"], "ObjectExpression": ["properties"], "Program": ["body"], diff --git a/test/fixtures/transformation/optional-classes-fast-super/accessing-super-class/actual.js b/test/fixtures/transformation/optional-classes-fast-super/accessing-super-class/actual.js new file mode 100644 index 0000000000..154d85730b --- /dev/null +++ b/test/fixtures/transformation/optional-classes-fast-super/accessing-super-class/actual.js @@ -0,0 +1,25 @@ +class Test extends Foo { + constructor() { + woops.super.test(); + super(); + super.test(); + + super(...arguments); + super("test", ...arguments); + + super.test(...arguments); + super.test("test", ...arguments); + } + + test() { + super(); + super(...arguments); + super("test", ...arguments); + } + + static foo() { + super(); + super(...arguments); + super("test", ...arguments); + } +} diff --git a/test/fixtures/transformation/optional-classes-fast-super/accessing-super-class/expected.js b/test/fixtures/transformation/optional-classes-fast-super/accessing-super-class/expected.js new file mode 100644 index 0000000000..63b24b422e --- /dev/null +++ b/test/fixtures/transformation/optional-classes-fast-super/accessing-super-class/expected.js @@ -0,0 +1,50 @@ +"use strict"; + +var _slice = Array.prototype.slice; +var _inherits = function (child, parent) { + if (typeof parent !== "function" && parent !== null) { + throw new TypeError("Super expression must either be null or a function, not " + typeof parent); + } + child.prototype = Object.create(parent && parent.prototype, { + constructor: { + value: child, + enumerable: false, + writable: true, + configurable: true + } + }); + if (parent) child.__proto__ = parent; +}; + +var Test = (function (Foo) { + var Test = function Test() { + var _Foo$prototype$test, _Foo$prototype$test2; + woops["super"].test(); + Foo.call(this); + Foo.prototype.test.call(this); + + Foo.call.apply(Foo, [this].concat(_slice.call(arguments))); + Foo.call.apply(Foo, [this, "test"].concat(_slice.call(arguments))); + + (_Foo$prototype$test = Foo.prototype.test).call.apply(_Foo$prototype$test, [this].concat(_slice.call(arguments))); + (_Foo$prototype$test2 = Foo.prototype.test).call.apply(_Foo$prototype$test2, [this, "test"].concat(_slice.call(arguments))); + }; + + _inherits(Test, Foo); + + Test.prototype.test = function () { + var _Foo$prototype$test3, _Foo$prototype$test4; + Foo.prototype.test.call(this); + (_Foo$prototype$test3 = Foo.prototype.test).call.apply(_Foo$prototype$test3, [this].concat(_slice.call(arguments))); + (_Foo$prototype$test4 = Foo.prototype.test).call.apply(_Foo$prototype$test4, [this, "test"].concat(_slice.call(arguments))); + }; + + Test.foo = function () { + var _Foo$foo, _Foo$foo2; + Foo.foo.call(this); + (_Foo$foo = Foo.foo).call.apply(_Foo$foo, [this].concat(_slice.call(arguments))); + (_Foo$foo2 = Foo.foo).call.apply(_Foo$foo2, [this, "test"].concat(_slice.call(arguments))); + }; + + return Test; +})(Foo); diff --git a/test/fixtures/transformation/optional-classes-fast-super/accessing-super-properties/actual.js b/test/fixtures/transformation/optional-classes-fast-super/accessing-super-properties/actual.js new file mode 100644 index 0000000000..524a05eaa4 --- /dev/null +++ b/test/fixtures/transformation/optional-classes-fast-super/accessing-super-properties/actual.js @@ -0,0 +1,6 @@ +class Test extends Foo { + constructor() { + super.test; + super.test.whatever; + } +} diff --git a/test/fixtures/transformation/optional-classes-fast-super/accessing-super-properties/expected.js b/test/fixtures/transformation/optional-classes-fast-super/accessing-super-properties/expected.js new file mode 100644 index 0000000000..bf1a718a95 --- /dev/null +++ b/test/fixtures/transformation/optional-classes-fast-super/accessing-super-properties/expected.js @@ -0,0 +1,27 @@ +"use strict"; + +var _inherits = function (child, parent) { + if (typeof parent !== "function" && parent !== null) { + throw new TypeError("Super expression must either be null or a function, not " + typeof parent); + } + child.prototype = Object.create(parent && parent.prototype, { + constructor: { + value: child, + enumerable: false, + writable: true, + configurable: true + } + }); + if (parent) child.__proto__ = parent; +}; + +var Test = (function (Foo) { + var Test = function Test() { + Foo.prototype.test; + Foo.prototype.test.whatever; + }; + + _inherits(Test, Foo); + + return Test; +})(Foo); diff --git a/test/fixtures/transformation/optional-classes-fast-super/calling-super-properties/actual.js b/test/fixtures/transformation/optional-classes-fast-super/calling-super-properties/actual.js new file mode 100644 index 0000000000..993109dc24 --- /dev/null +++ b/test/fixtures/transformation/optional-classes-fast-super/calling-super-properties/actual.js @@ -0,0 +1,10 @@ +class Test extends Foo { + constructor() { + super.test.whatever(); + super.test(); + } + + static test() { + return super.wow(); + } +} diff --git a/test/fixtures/transformation/optional-classes-fast-super/calling-super-properties/expected.js b/test/fixtures/transformation/optional-classes-fast-super/calling-super-properties/expected.js new file mode 100644 index 0000000000..f107cf4bc4 --- /dev/null +++ b/test/fixtures/transformation/optional-classes-fast-super/calling-super-properties/expected.js @@ -0,0 +1,31 @@ +"use strict"; + +var _inherits = function (child, parent) { + if (typeof parent !== "function" && parent !== null) { + throw new TypeError("Super expression must either be null or a function, not " + typeof parent); + } + child.prototype = Object.create(parent && parent.prototype, { + constructor: { + value: child, + enumerable: false, + writable: true, + configurable: true + } + }); + if (parent) child.__proto__ = parent; +}; + +var Test = (function (Foo) { + var Test = function Test() { + Foo.prototype.test.whatever(); + Foo.prototype.test.call(this); + }; + + _inherits(Test, Foo); + + Test.test = function () { + return Foo.wow.call(this); + }; + + return Test; +})(Foo); diff --git a/test/fixtures/transformation/optional-classes-fast-super/options.json b/test/fixtures/transformation/optional-classes-fast-super/options.json new file mode 100644 index 0000000000..a1ebbb1755 --- /dev/null +++ b/test/fixtures/transformation/optional-classes-fast-super/options.json @@ -0,0 +1,3 @@ +{ + "optional": ["classesFastSuper"] +} diff --git a/test/fixtures/transformation/optional-classes-fast-super/super-class-id-member-expression/actual.js b/test/fixtures/transformation/optional-classes-fast-super/super-class-id-member-expression/actual.js new file mode 100644 index 0000000000..e245d3a69b --- /dev/null +++ b/test/fixtures/transformation/optional-classes-fast-super/super-class-id-member-expression/actual.js @@ -0,0 +1,7 @@ +class BaseController extends Chaplin.Controller { + +} + +class BaseController2 extends Chaplin.Controller.Another { + +} diff --git a/test/fixtures/transformation/optional-classes-fast-super/super-class-id-member-expression/expected.js b/test/fixtures/transformation/optional-classes-fast-super/super-class-id-member-expression/expected.js new file mode 100644 index 0000000000..058b2d7217 --- /dev/null +++ b/test/fixtures/transformation/optional-classes-fast-super/super-class-id-member-expression/expected.js @@ -0,0 +1,40 @@ +"use strict"; + +var _inherits = function (child, parent) { + if (typeof parent !== "function" && parent !== null) { + throw new TypeError("Super expression must either be null or a function, not " + typeof parent); + } + child.prototype = Object.create(parent && parent.prototype, { + constructor: { + value: child, + enumerable: false, + writable: true, + configurable: true + } + }); + if (parent) child.__proto__ = parent; +}; + +var BaseController = (function (_Chaplin$Controller) { + var BaseController = function BaseController() { + if (Chaplin.Controller != null) { + Chaplin.Controller.apply(this, arguments); + } + }; + + _inherits(BaseController, _Chaplin$Controller); + + return BaseController; +})(Chaplin.Controller); + +var BaseController2 = (function (_Chaplin$Controller$Another) { + var BaseController2 = function BaseController2() { + if (Chaplin.Controller.Another != null) { + Chaplin.Controller.Another.apply(this, arguments); + } + }; + + _inherits(BaseController2, _Chaplin$Controller$Another); + + return BaseController2; +})(Chaplin.Controller.Another); diff --git a/test/fixtures/transformation/optional-classes-fast-super/super-class/actual.js b/test/fixtures/transformation/optional-classes-fast-super/super-class/actual.js new file mode 100644 index 0000000000..13a98b37be --- /dev/null +++ b/test/fixtures/transformation/optional-classes-fast-super/super-class/actual.js @@ -0,0 +1 @@ +class Test extends Foo { } diff --git a/test/fixtures/transformation/optional-classes-fast-super/super-class/expected.js b/test/fixtures/transformation/optional-classes-fast-super/super-class/expected.js new file mode 100644 index 0000000000..4628b9a5ef --- /dev/null +++ b/test/fixtures/transformation/optional-classes-fast-super/super-class/expected.js @@ -0,0 +1,28 @@ +"use strict"; + +var _inherits = function (child, parent) { + if (typeof parent !== "function" && parent !== null) { + throw new TypeError("Super expression must either be null or a function, not " + typeof parent); + } + child.prototype = Object.create(parent && parent.prototype, { + constructor: { + value: child, + enumerable: false, + writable: true, + configurable: true + } + }); + if (parent) child.__proto__ = parent; +}; + +var Test = (function (Foo) { + var Test = function Test() { + if (Foo != null) { + Foo.apply(this, arguments); + } + }; + + _inherits(Test, Foo); + + return Test; +})(Foo); diff --git a/test/fixtures/transformation/optional-classes-fast-super/super-function-fallback/actual.js b/test/fixtures/transformation/optional-classes-fast-super/super-function-fallback/actual.js new file mode 100644 index 0000000000..18ebeb8f30 --- /dev/null +++ b/test/fixtures/transformation/optional-classes-fast-super/super-function-fallback/actual.js @@ -0,0 +1,5 @@ +class Test { + constructor() { + super.hasOwnProperty("test"); + } +} diff --git a/test/fixtures/transformation/optional-classes-fast-super/super-function-fallback/expected.js b/test/fixtures/transformation/optional-classes-fast-super/super-function-fallback/expected.js new file mode 100644 index 0000000000..e17e74db06 --- /dev/null +++ b/test/fixtures/transformation/optional-classes-fast-super/super-function-fallback/expected.js @@ -0,0 +1,5 @@ +"use strict"; + +var Test = function Test() { + Function.prototype.hasOwnProperty.call(this, "test"); +};