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

@@ -71,18 +71,22 @@ var buildClass = function (node, generateUid) {
var buildClassBody = function (body, className, superName, node) {
var instanceMutatorMap = {};
var staticMutatorMap = {};
var staticMutatorMap = {};
var hasConstructor = false;
var construct = body[0];
var classBody = node.body.body;
_.each(classBody, function (node) {
var methodName = node.key.name;
var method = node.value;
replaceInstanceSuperReferences(superName, method);
replaceInstanceSuperReferences(superName, method, methodName);
if (methodName === "constructor") {
if (node.kind === "") {
addConstructor(body[0], method);
hasConstructor = true;
addConstructor(construct, method);
} else {
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)) {
var protoId = util.template("prototype-identifier", {
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;
node.name = superName.name || superName.value;
// super(); -> ClassName.call(this);
// super(); -> ClassName.prototype.MethodName.call(this);
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());
} 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");
traverse(method, function (node, parent) {
if (node.type === "Identifier" && node.name === "super") {
superIdentifier(superName, 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;
}
superIdentifier(superName, methodName, node, parent);
} else if (node.type === "CallExpression") {
var callee = node.callee;
if (callee.type !== "MemberExpression") 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());
} 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) {
construct.defaults = method.defaults;
construct.params = method.params;