Skip to content
This repository has been archived by the owner on May 29, 2018. It is now read-only.

Commit

Permalink
recv
Browse files Browse the repository at this point in the history
  • Loading branch information
sqs committed Jan 24, 2014
1 parent 164516f commit 96bec12
Show file tree
Hide file tree
Showing 42 changed files with 3,294 additions and 1,204 deletions.
9 changes: 1 addition & 8 deletions annotate.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ function resolve(file, ident, state) {
}

function getRefTarget(file, ident) {
if (ident._path) return getConcretePathTypeID(ident._path);

var expr;
try {
expr = tern.findQueryExpr(file, ident);
Expand All @@ -63,14 +61,9 @@ function getRefTarget(file, ident) {
}

var av = infer.expressionType(expr);
if (av.originNode && av.originNode._path) return getConcretePathTypeID(av.originNode._path);

if (av._path) return getConcretePathTypeID(av._path);
var type = av.getType(false);
if (!type) return null;
if (type.originNode && type.originNode._path) return getConcretePathTypeID(type.originNode._path);

if (type instanceof infer.Prim || type instanceof infer.Arr) return;

return getTypeID(type);
}

Expand Down
294 changes: 69 additions & 225 deletions dump.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,27 @@
var defnode = require('defnode');
var scopewalk = require('tern-scope-walk');
var infer = require('tern/lib/infer');
var symbol_id = require('./symbol_id');

exports.dump = function(origins, options) {
if (typeof origins == 'string') origins = [origins];
var state = new State(origins, options || {});
var state = new State(origins, options);

runPass(state.passes.preDumpReach, state);
runPass(state.passes.preCondenseReach, state);
var allOrigins = state.cx.parent.files.map(function(f) { return f.name; }).sort();

state.cx.topScope.path = "<top>";
state.cx.topScope.reached("", state);
for (var path in state.roots)
reach(state.roots[path], null, path, state);
for (var i = 0; i < state.patchUp.length; ++i)
patchUpSimpleInstance(state.patchUp[i], state);
var avals = scopewalk.walk(allOrigins);
for (var path in avals) {
var aval = avals[path];
aval.backprop();
if (!state.isTarget(aval.origin)) delete avals[path];
}

runPass(state.passes.postDumpReach, state);
runPass(state.passes.postDumpScopeWalk, state);

for (var path in state.types)
link(path, state.types[path], state);
for (var path in state.types)
store(path, state.types[path], state);
for (var path in state.altPaths)
storeAlt(path, state.altPaths[path], state);
var hasDef = false;
for (var _def in state.output['!define']) { hasDef = true; break; }
if (!hasDef) delete state.output['!define'];
var sortedPaths = Object.keys(avals).sort();

runPass(state.passes.postDump, state);
sortedPaths.forEach(function(path) {
state.output.push(describe(path, avals[path], state));
});

return state.output;
};
Expand All @@ -39,188 +32,82 @@ function State(origins, options) {
this.passes = options.passes || this.cx.parent && this.cx.parent.passes || {};
this.output = [];
this.options = options;
this.types = Object.create(null);
this.altPaths = Object.create(null);
this.patchUp = [];
this.roots = Object.create(null);
}

State.prototype.isTarget = function(origin) {
return this.origins.indexOf(origin) > -1;
};
}

State.prototype.getSpan = function(node) {
if (this.options.spans == false) return null;
if (node.span) return node.span;
if (!node.originNode) return null;
return node.originNode.start + '-' + node.originNode.end;
return node.start + '-' + node.end;
};

function pathLen(path) {
var len = 1, pos = 0, dot;
while ((dot = path.indexOf('.', pos)) != -1) {
pos = dot + 1;
len += path.charAt(pos) == '!' ? 10 : 1;
}
return len;
}

function isConcrete(path) {
return !/\!|<i>/.test(path);
}

function hop(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}

function isSimpleInstance(o) {
return o.proto && !(o instanceof infer.Fn) && o.proto != infer.cx().protos.Object &&
o.proto.hasCtor && !o.hasCtor;
}

function reach(type, path, id, state) {
var actual = type.getType(false);
if (!actual) return;
var orig = type.origin || actual.origin, relevant = false;
if (orig) {
relevant = state.isTarget(orig);
}
var newPath = path ? path + '.' + id : id, oldPath = actual.path;
var shorter = !oldPath || pathLen(oldPath) > pathLen(newPath);
if (shorter) {
if (!(actual instanceof infer.Prim)) actual.path = newPath;
if (actual.reached(newPath, state, !relevant) && relevant) {
var data = state.types[oldPath];
if (data) {
delete state.types[oldPath];
state.altPaths[oldPath] = actual;
} else data = {type: actual};
data.object = type;
data.doc = type.doc || (actual != type && state.isTarget(actual.origin) && type.doc) || data.doc;
data.data = actual.metaData;
data.relevant = relevant;
state.types[newPath] = data;
}
} else {
if (relevant) {
state.altPaths[newPath] = actual;
infer.AVal.prototype.backprop = function() {
function getForwardsRecursively(av, seen) {
if (!seen) seen = [];
if (av.forward) for (var i = 0; i < av.forward.length; i++) {
var fwd = av.forward[i].propagatesTo();
if (!fwd) continue;
var dest;
if (fwd instanceof infer.AVal) dest = fwd;
else if (fwd.target instanceof infer.AVal) {
// TODO(sqs): Handle propagations to properties of types that may or may
// not exist concretely. (fwd.pathExt is the key path.) See `function
// expore` in tern/lib/infer.js for how this is done in tern's inference engine.
} else continue;
if (dest && seen.indexOf(dest) == -1) {
seen.push(dest);
getForwardsRecursively(dest, seen);
}
}
return seen;
}

var allFwds = getForwardsRecursively(this);
for (var i = 0; i < allFwds.length; i++) {
var fwd = allFwds[i];
if (fwd == this) continue;
if (!fwd.recv) fwd.recv = [];
fwd.recv.push(this);
}
}
function reachTypeOnly(aval, path, id, state) {
var type = aval.getType();
if (type) reach(type, path, id, state);
}

infer.Prim.prototype.reached = function() {return true;};
function describe(path, aval, state) {
var o = {id: path, key: symbol_id.parse(path)};

infer.Arr.prototype.reached = function(path, state, concrete) {
if (!concrete) reachTypeOnly(this.getProp('<i>'), path, '<i>', state);
return true;
};
if (aval.recv) o.recv = aval.recv.map(function(av) { return av._path; }).sort();

infer.Fn.prototype.reached = function(path, state, concrete) {
infer.Obj.prototype.reached.call(this, path, state, concrete);
if (!concrete) {
for (var i = 0; i < this.args.length; ++i)
reachTypeOnly(this.args[i], path, '!' + i, state);
reachTypeOnly(this.retval, path, '!ret', state);
var type = aval.getType(true);
if (type) {
o.type = infer.toString(type, 1);
}
return true;
};

infer.Obj.prototype.reached = function(path, state, concrete) {
if (isSimpleInstance(this) && !this.dumpForceInclude) {
if (state.patchUp.indexOf(this) == -1) state.patchUp.push(this);
return true;
} else if (this.proto && !concrete) {
reach(this.proto, path, '!proto', state);
}
var hasProps = false;
for (var prop in this.props) {
reach(this.props[prop], path, prop, state);
hasProps = true;
}
if (!hasProps && !this.dumpForceInclude && !(this instanceof infer.Fn)) {
this.nameOverride = '?';
return false;
}
return true;
};
if (aval.doc) o.doc = aval.doc;

function patchUpSimpleInstance(obj, state) {
var path = obj.proto.hasCtor.path;
if (path) {
obj.nameOverride = '+' + path;
} else {
path = obj.path;
}
for (var prop in obj.props)
reach(obj.props[prop], path, prop, state);
}
var node = aval.originNode;
if (node) {
o.file = astNodeFilename(node);

function createPath(parts, state) {
var base = state.output;
for (var i = parts.length - 1; i >= 0; --i) if (!isConcrete(parts[i])) {
var def = parts.slice(0, i + 1).join('.');
var defs = state.output['!define'];
if (hop(defs, def)) base = defs[def];
else defs[def] = base = {};
parts = parts.slice(i + 1);
}
for (var i = 0; i < parts.length; ++i) {
if (hop(base, parts[i])) base = base[parts[i]];
else base = base[parts[i]] = {};
}
return base;
}
o.ident = state.getSpan(node);

function link(path, info, state) {
if (info.type && info.type.originNode) {
if (info.type.originNode._path && info.type.originNode._path != path) throw new Error('Type node path conflict: ' + info.type.originNode._path + ' and ' + path);
info.type.originNode._path = path;
info.type.originNode._pathFor = 'type ' + info.type.toString();
try {
var nameNodes = defnode.findNameNodes(info.type.originNode.sourceFile.ast, info.type.originNode.start, info.type.originNode.end);
if (nameNodes) {
info.type._identNodes = nameNodes;
nameNodes.forEach(function(n) {
if (n._path && n._path != path) throw new Error('Ident node path conflict: ' + n._path + ' and ' + path);
n._path = path;
n._pathFor = 'name node for type ' + info.type.toString();
});
}
var defn = defnode.findDefinitionNode(node.sourceFile.ast, node.start, node.end);
if (defn) o.defn = state.getSpan({start: Math.min(node.start, defn.start), end: Math.max(node.end, defn.end)});
} catch (e) {}
}

if (info.object && info.object.originNode) {
if (info.object.originNode._path && info.object.originNode._path != path) {
if (info.object.originNode._pathFor == 'object') {
// It's unavoidable that sometimes we'll have an AST node that's the
// originNode for multiple AVals. See testdata/node_path_conflict.js for
// an example of this. Just take the shorter one for now. TODO(sqs):
// improve this.
var shorter = info.object.originNode._path.length >= path.length;
if (!shorter) return;
} else {
throw new Error('Object node path conflict: ' + info.object.originNode._path + ' (path for ' + (info.object.originNode._pathFor || 'unknown') + ') and ' + path + ' (at ' + info.object.originNode.sourceFile.name + ':' + info.object.originNode.start + '-' + info.object.originNode.end + ')');
}
}
info.object.originNode._path = path;
info.object.originNode._pathFor = 'object';
try {
var defNode = defnode.findDefinitionNode(info.object.originNode.sourceFile.ast, info.object.originNode.start, info.object.originNode.end);
if (defNode) {
info.object.originNode._bodyNode = defNode;
if (defNode._path && defNode._path != path) throw new Error('Def node path conflict: ' + defNode._path + ' and ' + path);
defNode._path = path;
defNode._pathFor = 'def body node for object';
}
} catch (e) {}
}
o.exported = path.indexOf('@') == -1;
o.data = aval.metaData;

return o;
}

function store(path, info, state) {
function store2(path, info, state) {
if (!info.relevant) return;
var out = {path: path, id: symbol_id.parse(path)};
var name = typeName(info.type);
Expand All @@ -242,13 +129,6 @@ function store(path, info, state) {
if (info.object) {
var objFile;
if (info.object.originNode) {
if (!info.object.originNode.sourceFile) {
// Sometimes a node doesn't have a sourceFile property but one of its
// child nodes does. In that case, get sourceFile from the child node.
// TODO(sqs): why does this occur?
var childNode = info.object.originNode.property || info.object.originNode.argument || info.object.originNode.left;
info.object.originNode.sourceFile = childNode.sourceFile;
}
objFile = info.object.originNode.sourceFile.name;
}
out.objectDef = {file: objFile};
Expand All @@ -262,52 +142,16 @@ function store(path, info, state) {
state.output.push(out);
}

// TODO(sqs): make this emit refs and locals

function storeAlt(path, type, state) {
state.output[path] = type.nameOverride || type.path;
}

var typeNameStack = [];
function typeName(type) {
var actual = type.getType(false);
if (!actual || typeNameStack.indexOf(actual) > -1)
return actual && actual.path || '?';
typeNameStack.push(actual);
var name = actual.typeName();
typeNameStack.pop();
return name;
}

infer.Prim.prototype.typeName = function() { return this.name; };

infer.Arr.prototype.typeName = function() {
return '[' + typeName(this.getProp('<i>')) + ']';
};

infer.Fn.prototype.typeName = function() {
var out = 'fn(';
for (var i = 0; i < this.args.length; ++i) {
if (i) out += ', ';
var name = this.argNames[i];
if (name && name != '?') out += name + ': ';
out += typeName(this.args[i]);
}
out += ')';
if (this.computeRetSource) {
out += ' -> ' + this.computeRetSource;
} else if (!this.retval.isEmpty()) {
var rettype = this.retval.getType(false);
if (rettype) out += ' -> ' + typeName(rettype);
function astNodeFilename(node) {
// Sometimes a node doesn't have a sourceFile property but one of its
// child nodes does. In that case, get sourceFile from the child node.
// TODO(sqs): why does this occur?
if (!node.sourceFile) {
var childNode = node.property || node.argument || node.left;
node.sourceFile = childNode.sourceFile;
}
return out;
};

infer.Obj.prototype.typeName = function() {
if (this.nameOverride) return this.nameOverride;
if (!this.path) return '?';
return this.path;
};
return node.sourceFile.name;
}

function runPass(functions) {
if (functions) for (var i = 0; i < functions.length; ++i)
Expand Down
Loading

0 comments on commit 96bec12

Please sign in to comment.