make super behaviour more spec compliant - fixes #32

This commit is contained in:
Sebastian McKenzie
2014-10-10 15:49:28 +11:00
parent 35bb0bc71d
commit fb67ab9b5d
15 changed files with 132 additions and 33 deletions

View File

@@ -0,0 +1,2 @@
SUPER_NAME.call(this, arguments);

View File

@@ -71,18 +71,22 @@ var buildClass = function (node, generateUid) {
var buildClassBody = function (body, className, superName, node) { var buildClassBody = function (body, className, superName, node) {
var instanceMutatorMap = {}; var instanceMutatorMap = {};
var staticMutatorMap = {}; var staticMutatorMap = {};
var hasConstructor = false;
var construct = body[0];
var classBody = node.body.body; var classBody = node.body.body;
_.each(classBody, function (node) { _.each(classBody, function (node) {
var methodName = node.key.name; var methodName = node.key.name;
var method = node.value; var method = node.value;
replaceInstanceSuperReferences(superName, method); replaceInstanceSuperReferences(superName, method, methodName);
if (methodName === "constructor") { if (methodName === "constructor") {
if (node.kind === "") { if (node.kind === "") {
addConstructor(body[0], method); hasConstructor = true;
addConstructor(construct, method);
} else { } else {
throw util.errorWithNode(node, "unknown kind for constructor method"); throw util.errorWithNode(node, "unknown kind for constructor method");
} }
@@ -103,6 +107,12 @@ var buildClassBody = function (body, className, superName, node) {
} }
}); });
if (!hasConstructor && superName) {
construct.body.body.push(util.template("class-super-constructor-call", {
SUPER_NAME: superName
}, true));
}
if (!_.isEmpty(instanceMutatorMap)) { if (!_.isEmpty(instanceMutatorMap)) {
var protoId = util.template("prototype-identifier", { var protoId = util.template("prototype-identifier", {
CLASS_NAME: className CLASS_NAME: className
@@ -116,51 +126,46 @@ var buildClassBody = function (body, className, superName, node) {
} }
}; };
var superIdentifier = function (superName, node, parent) { var superIdentifier = function (superName, methodName, node, parent) {
if (parent.property === node) return; if (parent.property === node) return;
node.name = superName.name || superName.value; node.name = superName.name || superName.value;
// super(); -> ClassName.call(this); // super(); -> ClassName.prototype.MethodName.call(this);
if (parent.type === "CallExpression" && parent.callee === node) { if (parent.type === "CallExpression" && parent.callee === node) {
node.name += ".call"; if (methodName === "constructor") {
// constructor() { super(); }
node.name += ".call";
} else {
// foo() { super(); }
node.name += ".prototype." + methodName + ".call";
}
parent.arguments.unshift(b.thisExpression()); parent.arguments.unshift(b.thisExpression());
} else if (parent.type === "MemberExpression") {
// super.test -> ClassName.prototype.test
node.name += ".prototype";
} }
}; };
var replaceInstanceSuperReferences = function (superName, method) { var replaceInstanceSuperReferences = function (superName, method, methodName) {
superName = superName || b.literal("Function"); superName = superName || b.literal("Function");
traverse(method, function (node, parent) { traverse(method, function (node, parent) {
if (node.type === "Identifier" && node.name === "super") { if (node.type === "Identifier" && node.name === "super") {
superIdentifier(superName, node, parent); superIdentifier(superName, methodName, node, parent);
} else if (node.type === "MemberExpression") {
// no accessing of super properties
if (isAccessingSuperProperties(parent, node)) {
throw util.errorWithNode(node, "cannot access super properties");
} else {
return;
}
} else if (node.type === "CallExpression") { } else if (node.type === "CallExpression") {
var callee = node.callee; var callee = node.callee;
if (callee.type !== "MemberExpression") return; if (callee.type !== "MemberExpression") return;
if (callee.object.name !== "super") return; if (callee.object.name !== "super") return;
callee.property.name = "prototype." + callee.property.name + ".call"; // super.test(); -> Classname.prototype.MethodName.call(this);
callee.property.name = callee.property.name + ".call";
node.arguments.unshift(b.thisExpression()); node.arguments.unshift(b.thisExpression());
} else {
return;
} }
}); });
}; };
var isAccessingSuperProperties = function (parent, node) {
var obj = node.object;
return obj.type === "Identifier" && obj.name === "super" &&
parent.object === node;
};
var addConstructor = function (construct, method) { var addConstructor = function (construct, method) {
construct.defaults = method.defaults; construct.defaults = method.defaults;
construct.params = method.params; construct.params = method.params;

View File

@@ -1,7 +1,7 @@
{ {
"name": "6to5", "name": "6to5",
"description": "Turn ES6 code into vanilla ES5 with source maps and no runtime", "description": "Turn ES6 code into vanilla ES5 with source maps and no runtime",
"version": "1.7.4", "version": "1.7.5",
"author": "Sebastian McKenzie <sebmck@gmail.com>", "author": "Sebastian McKenzie <sebmck@gmail.com>",
"homepage": "https://github.com/sebmck/6to5", "homepage": "https://github.com/sebmck/6to5",
"repository": { "repository": {

50
test/bin.js Normal file
View File

@@ -0,0 +1,50 @@
var child = require("child_process");
var fs = require("fs");
var tmpLoc = __dirname + "/tmp";
var readTree = function () {
};
var run = function (name, args, callback) {
args = [__dirname + "/../bin." + name].concat(args);
var spawn = child.spawn(process.execPath, args);
var data = "";
spawn.stdout.on("write", function (chunk) {
data += chunk;
});
spawn.on("close", function () {
callback(data);
});
};
before(function () {
if (!fs.existsSync(tmpLoc)) fs.mkdirSync(tmpLoc);
process.chdir(tmpLoc);
});
suite("bin/6to5", function () {
test("--source-maps-inline");
test("--source-maps");
test("--whitelist");
test("--blacklist");
test("--out-file");
test("--out-dir");
test("stdout");
});
suite("bin/6to5-node", function () {
test("--eval");
test("--print");
});

View File

@@ -5,4 +5,8 @@ class Test extends Foo {
super.test(); super.test();
foob(super); foob(super);
} }
test() {
super();
}
} }

View File

@@ -14,5 +14,8 @@ var Test = function (Foo) {
} }
}); });
Test.__proto__ = Foo; Test.__proto__ = Foo;
Test.prototype.test = function () {
Foo.prototype.test.call(this);
};
return Test; return Test;
}(Foo); }(Foo);

View File

@@ -1,5 +1,6 @@
class Test extends Foo { class Test extends Foo {
constructor() { constructor() {
super.test;
super.test.whatever; super.test.whatever;
} }
} }

View File

@@ -0,0 +1,18 @@
var Test = function(Foo) {
function Test() {
Foo.prototype.test;
Foo.prototype.test.whatever;
}
Test.prototype = Object.create(Foo.prototype, {
constructor: {
value: Test,
enumerable: false,
writable: true,
configurable: true
}
});
Test.__proto__ = Foo;
return Test;
}(Foo);

View File

@@ -1,5 +1,6 @@
class Test extends Foo { class Test extends Foo {
constructor() { constructor() {
super.test.whatever(); super.test.whatever();
super.test();
} }
} }

View File

@@ -0,0 +1,16 @@
var Test = function(Foo) {
function Test() {
Foo.prototype.test.whatever();
Foo.prototype.test.call(this);
}
Test.prototype = Object.create(Foo.prototype, {
constructor: {
value: Test,
enumerable: false,
writable: true,
configurable: true
}
});
Test.__proto__ = Foo;
return Test;
}(Foo);

View File

@@ -1,3 +0,0 @@
{
"throws": "cannot access super properties"
}

View File

@@ -1,3 +0,0 @@
{
"throws": "cannot access super properties"
}

View File

@@ -1,5 +1,6 @@
var BaseController = function (Chaplin) { var BaseController = function (Chaplin) {
function BaseController() { function BaseController() {
Chaplin.Controller.call(this, arguments);
} }
BaseController.prototype = Object.create(Chaplin.Controller.prototype, { BaseController.prototype = Object.create(Chaplin.Controller.prototype, {
constructor: { constructor: {
@@ -15,6 +16,7 @@ var BaseController = function (Chaplin) {
var BaseController2 = function (Chaplin) { var BaseController2 = function (Chaplin) {
function BaseController2() { function BaseController2() {
Chaplin.Controller.Another.call(this, arguments);
} }
BaseController2.prototype = Object.create(Chaplin.Controller.Another.prototype, { BaseController2.prototype = Object.create(Chaplin.Controller.Another.prototype, {
constructor: { constructor: {

View File

@@ -1,5 +1,7 @@
var Q = function(_ref) { var Q = function(_ref) {
function Q() {} function Q() {
_ref.call(this, arguments);
}
Q.prototype = Object.create(_ref.prototype, { Q.prototype = Object.create(_ref.prototype, {
constructor: { constructor: {

View File

@@ -1,5 +1,6 @@
var Test = function (Foo) { var Test = function (Foo) {
function Test() { function Test() {
Foo.call(this, arguments);
} }
Test.prototype = Object.create(Foo.prototype, { Test.prototype = Object.create(Foo.prototype, {
constructor: { constructor: {