Created
June 8, 2016 18:46
-
-
Save Nnubes256/edc27814c0777926aa31e22b4f491cef to your computer and use it in GitHub Desktop.
classfinder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var fs = require('fs'); | |
var cytoscape = require('cytoscape'); | |
var acorn = require('acorn'); | |
var ONLY_PARSE = true; | |
var ONLY_AST = false; | |
var MODULE_MODULE = "C"; | |
var MODULE_REQUIRE = "F"; | |
var MODULE_DEFINE = "v"; | |
var MODULE_EMPTY = "G"; | |
var cy; | |
var file; | |
var matches = []; | |
var matchesAcc = 0; | |
var regex = new RegExp( | |
'(ig\\.' + | |
MODULE_MODULE + | |
'\\("([a-z0-9-.]+)"\\)(\\.' + | |
MODULE_REQUIRE + | |
'\\(([a-z0-9.," -]+)\\))?)', | |
"g" | |
); | |
/* | |
var ImpactImpostor = function() { | |
this.className = ""; | |
this.extensions = []; | |
this.self = this; | |
}; | |
ImpactImpostor.prototype.C = function(className) { | |
if(typeof className !== 'string') | |
throw new Error("Class is not defined as an string"); | |
this.className = className; | |
return this.self; // HACK | |
}; | |
ImpactImpostor.prototype.D = function() { | |
for (var i = 0; i < arguments.length; i++) { | |
if(typeof arguments[i] !== 'string') | |
throw new Error("Extension is not defined as an string!"); | |
this.extensions.push(arguments[i]); | |
} | |
}; | |
function evalInContext(js, context) { | |
return function() { return eval(js); }.call(context); | |
} | |
function evaluateClassDef(line) { | |
'use strict'; | |
var impact = new ImpactImpostor(); | |
var context = { | |
ig: impact | |
}; | |
var result = evalInContext(line, context); | |
return {className: impact.class, extensions: impact.extensions}; | |
} | |
*/ | |
Object.size = function(obj) { | |
var size = 0, key; | |
for (key in obj) { | |
if (obj.hasOwnProperty(key)) size++; | |
} | |
return size; | |
}; | |
if (!Array.prototype.first) | |
{ | |
Array.prototype.first = function(predicate) | |
{ | |
"use strict"; | |
if (this === null) | |
throw new TypeError(); | |
if (typeof predicate != "function") | |
throw new TypeError(); | |
for (var i = 0; i < this.length; i++) { | |
if (predicate(this[i])) { | |
return match; | |
} | |
} | |
return null; | |
}; | |
} | |
function escapeRegExp(string) { | |
return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); | |
} | |
String.prototype.replaceAll = function(find, replace) { | |
return this.replace(new RegExp(escapeRegExp(find), 'g'), replace); | |
}; | |
process.stderr.write('Reading javascript...'); | |
// Read compiled game | |
fs.readFile('game.compiled.js', 'utf8', function (err, rawData) { | |
if (err) { | |
throw err; | |
} | |
process.stderr.write('processing'); | |
var data = rawData.replaceAll('\n',' '); | |
var i = 0; | |
//var truths = 0; | |
//var empty = 0; | |
//var non_empty = 0; | |
var bad = false; | |
var fZoneChar; | |
var fZone; | |
var fBody = ''; | |
var EMPTY_FUNC = '(' + MODULE_EMPTY + '())'; | |
var FUNC = '(function(){'; | |
var time = process.hrtime(); | |
var ext; | |
while ((match = regex.exec(data)) !== null) { | |
fBody = ''; | |
fZoneChar = match.index + match[0].length + 2; | |
fZone = data.charAt(fZoneChar); | |
if(data.substring(fZoneChar, fZoneChar+5) == EMPTY_FUNC) { | |
//empty++; | |
} else if(data.substring(fZoneChar, fZoneChar+12) == FUNC) { | |
//non_empty++; | |
var pos = fZoneChar+12; | |
var done = false; | |
var braceAcc = 0; | |
var char; | |
i = 0; | |
while(!done) { | |
char = data.charAt(pos+i); | |
if(char == '}') { | |
if(braceAcc === 0) { | |
done = true; | |
} | |
else braceAcc--; | |
} | |
if(char == '{') braceAcc++; | |
if(!done) fBody += char; | |
//process.stderr.cursorTo(0); | |
//process.stderr.write(i.toString()); | |
i++; | |
} | |
//process.stderr.write('\n'); | |
} else { | |
console.error( | |
"Unknown function found: " + | |
data.substring( | |
fZoneChar - match[0].length - 2, fZoneChar+100 | |
) | |
); | |
bad = true; | |
} | |
if(match[4]) | |
ext = match[4].replaceAll('"','').replaceAll(" ", "").split(','); | |
else ext = []; | |
matches[matchesAcc] = { | |
moduleName: match[2], | |
extensions: ext, | |
body: (fBody === '' ? null : fBody) | |
}; | |
if(bad) { | |
console.error(matches[matchesAcc]); | |
} | |
bad = false; | |
matchesAcc++; | |
process.stderr.write('.'); | |
//fZone == '(' ? truths++ : console.error(fZone); | |
} | |
//console.error(empty + non_empty); | |
var diff = process.hrtime(time); | |
console.error('done!'); | |
console.error( | |
'Found and parsed ' + matchesAcc + ' modules in ' + | |
(diff[1] / 1000000) + ' ms' | |
); | |
if(ONLY_PARSE) { | |
console.log(JSON.stringify(matches)); | |
process.exit(0); | |
} | |
process.stderr.write("Processing function bodies (SEMI-STUB)"); | |
var body; | |
var ast; | |
var html = "<!DOCTYPE html><html><body>"; | |
for (i = 0; i < matches.length; i++) { | |
body = matches[i].body; | |
if(!body) continue; | |
try { | |
ast = acorn.parse(body); | |
} catch(e) { | |
console.error("\n"); | |
console.error(body); | |
console.error(""); | |
console.error(""); | |
throw e; | |
} | |
process.stdout.write(JSON.stringify(ast)); | |
process.stdout.write("\n\n"); | |
process.stderr.write("."); | |
} | |
html += "</body></html>"; | |
console.error("done!"); | |
if (ONLY_AST) process.exit(0); | |
console.error("Computing graph..."); | |
var posAcc = 0; | |
var extAcc = 0; | |
var edgAcc = 0; | |
var idAcc = 0; | |
var elements = { | |
nodes: [], | |
edges: [] | |
}; | |
/* | |
var sortedMatches = matches.sort(function(a,b) { | |
if(a.extensions.length === 0 && b.extensions.length === 0) { | |
return 0; // Both have no extensions | |
} | |
if(a.extensions.length === 0) { | |
return -1; // A has no extensions | |
} | |
if(b.extensions.length === 0) { | |
return 1; // B has no extensions | |
} | |
return 0; // Both have extensions | |
}); | |
*/ | |
// Add fake DOM ready node | |
elements.nodes[0] = { | |
group: "nodes", | |
data: { | |
id: "dom.ready", | |
name: "dom.ready", | |
moduleName: "dom.ready", | |
extensions: [] | |
}, | |
position: { x: posAcc, y: 0 }, | |
}; | |
posAcc++; | |
for (i = 0; i < matches.length; i++) { | |
process.stderr.cursorTo(0); | |
process.stderr.write("Adding nodes: " + (i+1)); | |
elements.nodes[i+1] = { | |
group: "nodes", | |
data: { | |
id: matches[i].moduleName, | |
name: matches[i].moduleName, | |
moduleName: matches[i].moduleName, | |
extensions: matches[i].extensions, | |
}, | |
position: { x: posAcc, y: 0 }, | |
}; | |
posAcc++; | |
} | |
process.stderr.write(" done!\n"); | |
var matchesProcessed = []; | |
var matchExts; | |
var extsMatch; | |
var extsInitialized = true; | |
var matchIndex = 0; | |
for (i = 0; i < matches.length; i++) { | |
process.stderr.cursorTo(0); | |
process.stderr.write("Adding edges: " + (i+1)); | |
matchExts = matches[i].extensions; | |
if (matchExts.length === 0) { | |
matchesProcessed.push(matches[i]); | |
continue; | |
} else { | |
for (var j = 0; j < matchExts.length; j++) { | |
elements.edges.push({ | |
data: { | |
id: ('e'+edgAcc), | |
source: matches[i].moduleName, | |
target: matchExts[j] | |
} | |
}); | |
edgAcc++; | |
} | |
matchesProcessed.push(matches[i]); | |
continue; | |
} | |
} | |
process.stderr.write(" done!\n"); | |
process.stderr.write("Loading cytoscape..."); | |
cytoscape({ | |
elements: elements, | |
ready: function(){ | |
cy = this; | |
process.stderr.write('loaded...'); | |
graph(cy); | |
} | |
}); | |
function end() { | |
for (i = 0; i < matches.length; i++) { | |
var exts = matches[i].extensions; | |
console.log( | |
'Class ' + | |
matches[i].moduleName + | |
' Extends ' + | |
(exts.length === 0 ? 'None' : exts.join(' ')) | |
); | |
} | |
} | |
}); | |
function graph(cy) { | |
process.stderr.write("doing layout..."); | |
cy.layout({ name: 'circle' }); | |
var json = cy.json().elements; | |
console.error("done!"); | |
console.log(JSON.stringify(json)+"\n\n"); | |
//end(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment