Skip to content

Commit

Permalink
support nested classes
Browse files Browse the repository at this point in the history
  • Loading branch information
Swatinem committed Sep 2, 2012
1 parent 35ebea4 commit 989117d
Show file tree
Hide file tree
Showing 5 changed files with 360 additions and 241 deletions.
147 changes: 87 additions & 60 deletions demo/lib/harmonizr.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,77 +261,31 @@ function processArrowFunctions(modifier, options) {
}

function processClasses(modifier, options) {
var ast = modifier.ast;
var lines = modifier.lines;
var ast, classes;

var classes = [];
traverse(ast, function(node) {
if (node.type === Syntax.ClassDeclaration || node.type === Syntax.ClassExpression) {
classes.push(node);
do {
ast = modifier.ast;
classes = findClasses();

if (!classes.length) {
return;
}
});

classes.forEach(function(node) {
var node = classes[0];

var name = node.id && node.id.name || '__klass';
var methods = [];
traverse(node, function(innerNode) {
if (innerNode.type === Syntax.MethodDefinition) {
methods.push(innerNode);
}/* FIXME: can classes be nested? else if (innerNode !== node && innerNode.type === Syntax.ClassDeclaration) {
return false;
}*/
});
traverse(node, findMethods);
methods.reverse();

// Replace the trailing } with a return for the IIFE pattern
modifier.replace({line: node.body.loc.end.line, column: node.body.loc.end.column - 1}, node.loc.end,
'; return ' + name + ';})()' + (node.type === Syntax.ClassDeclaration ? ';' : ''));

methods.forEach(function(method) {
var supers = [];
// collect all `super` Identifiers of calls and `super.X` MemberExpressions
traverse(method, function(innerNode) {
if ((innerNode.type === Syntax.CallExpression &&
innerNode.callee.type === Syntax.Identifier &&
innerNode.callee.name === 'super') ||
(innerNode.type === Syntax.MemberExpression &&
innerNode.object.type === Syntax.Identifier &&
innerNode.object.name === 'super')) {
supers.push(innerNode);
}/* FIXME: can classes be nested? else if (innerNode !== node && innerNode.type === Syntax.ClassDeclaration) {
return false;
}*/
});
supers.reverse();
supers.forEach(function(superItem) {
if (superItem.type === Syntax.CallExpression) {
// super(X) -> X__super.bind(this)(X)
modifier.replace(superItem.callee.loc.start, superItem.callee.loc.end,
name + '__super.bind(this)');
} else {
// super.method -> X__prototype.method.bind(this)
modifier.insert(superItem.property.loc.end,
'.bind(this)');
modifier.replace(superItem.object.loc.start, superItem.object.loc.end,
name + '__prototype');
}
});

if (method.key.name === 'constructor') {
modifier.replace(method.key.loc.start, method.key.loc.end,
'function ' + name);
} else {
modifier.replace(method.key.loc.start, method.key.loc.end,
name + '.prototype.' + method.key.name + ' = function');
}
// TODO: get and set methods
});
methods.forEach(processMethod);

// In case we have no constructor, insert an empty function
var hasConstructor = methods.some(function(method) {
return method.key.name === 'constructor';
});
if (!hasConstructor) {
if (!hasConstructor()) {
modifier.insert({line: node.body.loc.start.line, column: node.body.loc.start.column + 1},
'function ' + name + '() {};');
}
Expand All @@ -357,13 +311,86 @@ function processClasses(modifier, options) {

// Replace start until the super class with IIFE pattern
modifier.replace(node.loc.start, node.superClass.loc.start,
'var ' + name + ' = (function () {');
(node.type === Syntax.ClassDeclaration ? 'var ' + name + ' = ' : '') + '(function () {');
} else {
// Replace start of the ClassDeclaration with IIFE pattern
modifier.replace(node.loc.start, node.body.loc.start,
(node.type === Syntax.ClassDeclaration ? 'var ' + name + ' = ' : '') + '(function () ');
}
});

if (classes.length <= 1) {
return;
}
modifier.refresh();
} while (true);

function isClass(node) {
return node.type === Syntax.ClassDeclaration || node.type === Syntax.ClassExpression;
}
function findClasses() {
var classes = [];
traverse(ast, function(node) {
if (isClass(node)) {
classes.push(node);
}
});
return classes;
}

function findMethods(innerNode) {
if (innerNode.type === Syntax.MethodDefinition) {
methods.push(innerNode);
} else if (innerNode !== node && isClass(innerNode)) {
return false;
}
}

function processMethod(method) {
var supers = [];
// collect all `super` Identifiers of calls and `super.X` MemberExpressions
traverse(method, function(innerNode) {
if ((innerNode.type === Syntax.CallExpression &&
innerNode.callee.type === Syntax.Identifier &&
innerNode.callee.name === 'super') ||
(innerNode.type === Syntax.MemberExpression &&
innerNode.object.type === Syntax.Identifier &&
innerNode.object.name === 'super')) {
supers.push(innerNode);
} else if (innerNode !== node && isClass(innerNode)) {
return false;
}
});
supers.reverse();
supers.forEach(function(superItem) {
if (superItem.type === Syntax.CallExpression) {
// super(X) -> X__super.bind(this)(X)
modifier.replace(superItem.callee.loc.start, superItem.callee.loc.end,
name + '__super.bind(this)');
} else {
// super.method -> X__prototype.method.bind(this)
modifier.insert(superItem.property.loc.end,
'.bind(this)');
modifier.replace(superItem.object.loc.start, superItem.object.loc.end,
name + '__prototype');
}
});

if (method.key.name === 'constructor') {
modifier.replace(method.key.loc.start, method.key.loc.end,
'function ' + name);
} else {
modifier.replace(method.key.loc.start, method.key.loc.end,
name + '.prototype.' + method.key.name + ' = function');
}
// TODO: get and set methods
}

function hasConstructor() {
return methods.some(function(method) {
return method.key.name === 'constructor';
});
}

}

function processDestructuringAssignments(modifier, options) {
Expand Down
147 changes: 87 additions & 60 deletions lib/harmonizr.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,77 +261,31 @@ function processArrowFunctions(modifier, options) {
}

function processClasses(modifier, options) {
var ast = modifier.ast;
var lines = modifier.lines;
var ast, classes;

var classes = [];
traverse(ast, function(node) {
if (node.type === Syntax.ClassDeclaration || node.type === Syntax.ClassExpression) {
classes.push(node);
do {
ast = modifier.ast;
classes = findClasses();

if (!classes.length) {
return;
}
});

classes.forEach(function(node) {
var node = classes[0];

var name = node.id && node.id.name || '__klass';
var methods = [];
traverse(node, function(innerNode) {
if (innerNode.type === Syntax.MethodDefinition) {
methods.push(innerNode);
}/* FIXME: can classes be nested? else if (innerNode !== node && innerNode.type === Syntax.ClassDeclaration) {
return false;
}*/
});
traverse(node, findMethods);
methods.reverse();

// Replace the trailing } with a return for the IIFE pattern
modifier.replace({line: node.body.loc.end.line, column: node.body.loc.end.column - 1}, node.loc.end,
'; return ' + name + ';})()' + (node.type === Syntax.ClassDeclaration ? ';' : ''));

methods.forEach(function(method) {
var supers = [];
// collect all `super` Identifiers of calls and `super.X` MemberExpressions
traverse(method, function(innerNode) {
if ((innerNode.type === Syntax.CallExpression &&
innerNode.callee.type === Syntax.Identifier &&
innerNode.callee.name === 'super') ||
(innerNode.type === Syntax.MemberExpression &&
innerNode.object.type === Syntax.Identifier &&
innerNode.object.name === 'super')) {
supers.push(innerNode);
}/* FIXME: can classes be nested? else if (innerNode !== node && innerNode.type === Syntax.ClassDeclaration) {
return false;
}*/
});
supers.reverse();
supers.forEach(function(superItem) {
if (superItem.type === Syntax.CallExpression) {
// super(X) -> X__super.bind(this)(X)
modifier.replace(superItem.callee.loc.start, superItem.callee.loc.end,
name + '__super.bind(this)');
} else {
// super.method -> X__prototype.method.bind(this)
modifier.insert(superItem.property.loc.end,
'.bind(this)');
modifier.replace(superItem.object.loc.start, superItem.object.loc.end,
name + '__prototype');
}
});

if (method.key.name === 'constructor') {
modifier.replace(method.key.loc.start, method.key.loc.end,
'function ' + name);
} else {
modifier.replace(method.key.loc.start, method.key.loc.end,
name + '.prototype.' + method.key.name + ' = function');
}
// TODO: get and set methods
});
methods.forEach(processMethod);

// In case we have no constructor, insert an empty function
var hasConstructor = methods.some(function(method) {
return method.key.name === 'constructor';
});
if (!hasConstructor) {
if (!hasConstructor()) {
modifier.insert({line: node.body.loc.start.line, column: node.body.loc.start.column + 1},
'function ' + name + '() {};');
}
Expand All @@ -357,13 +311,86 @@ function processClasses(modifier, options) {

// Replace start until the super class with IIFE pattern
modifier.replace(node.loc.start, node.superClass.loc.start,
'var ' + name + ' = (function () {');
(node.type === Syntax.ClassDeclaration ? 'var ' + name + ' = ' : '') + '(function () {');
} else {
// Replace start of the ClassDeclaration with IIFE pattern
modifier.replace(node.loc.start, node.body.loc.start,
(node.type === Syntax.ClassDeclaration ? 'var ' + name + ' = ' : '') + '(function () ');
}
});

if (classes.length <= 1) {
return;
}
modifier.refresh();
} while (true);

function isClass(node) {
return node.type === Syntax.ClassDeclaration || node.type === Syntax.ClassExpression;
}
function findClasses() {
var classes = [];
traverse(ast, function(node) {
if (isClass(node)) {
classes.push(node);
}
});
return classes;
}

function findMethods(innerNode) {
if (innerNode.type === Syntax.MethodDefinition) {
methods.push(innerNode);
} else if (innerNode !== node && isClass(innerNode)) {
return false;
}
}

function processMethod(method) {
var supers = [];
// collect all `super` Identifiers of calls and `super.X` MemberExpressions
traverse(method, function(innerNode) {
if ((innerNode.type === Syntax.CallExpression &&
innerNode.callee.type === Syntax.Identifier &&
innerNode.callee.name === 'super') ||
(innerNode.type === Syntax.MemberExpression &&
innerNode.object.type === Syntax.Identifier &&
innerNode.object.name === 'super')) {
supers.push(innerNode);
} else if (innerNode !== node && isClass(innerNode)) {
return false;
}
});
supers.reverse();
supers.forEach(function(superItem) {
if (superItem.type === Syntax.CallExpression) {
// super(X) -> X__super.bind(this)(X)
modifier.replace(superItem.callee.loc.start, superItem.callee.loc.end,
name + '__super.bind(this)');
} else {
// super.method -> X__prototype.method.bind(this)
modifier.insert(superItem.property.loc.end,
'.bind(this)');
modifier.replace(superItem.object.loc.start, superItem.object.loc.end,
name + '__prototype');
}
});

if (method.key.name === 'constructor') {
modifier.replace(method.key.loc.start, method.key.loc.end,
'function ' + name);
} else {
modifier.replace(method.key.loc.start, method.key.loc.end,
name + '.prototype.' + method.key.name + ' = function');
}
// TODO: get and set methods
}

function hasConstructor() {
return methods.some(function(method) {
return method.key.name === 'constructor';
});
}

}

function processDestructuringAssignments(modifier, options) {
Expand Down
Loading

0 comments on commit 989117d

Please sign in to comment.