Object.prototype.toString = function () {return uneval(this)}

const CALLGRAPH_FILE = "/tmp/callgraph.js"
//need a per-func virtual table
//layout is like..id is srcid {calls: id list; overriden_by: id list; name, etc }
var graph = eval(read_file(CALLGRAPH_FILE)) || {calls:{},inheritance:{},files:[], types:[]}
var my_decl;
const MEM_NAME = 0
const MEM_TYPE = 1
const OVERRIDE_LS = 3

function add_item(array, item) {
  var i = array.indexOf(item);
  if(i == -1) {
  	i = array.length
	array.push(item)
  }
  return i
}
function shorten(id) {
  var pos = id.lastIndexOf(":")
  var file = id.substring(0, pos)
  var line = parseInt(id.substring(pos + 1, id.length))
  return [add_item(graph.files, file), line]
}

function process_v(v) {
  if(!v.isFcall) return
  if(v.isDeref) {
    print(v)
    return
  }
  out_edges = graph.calls[my_decl]
  if(!out_edges)
    graph.calls[my_decl] = out_edges = []
  srcid = shorten(v.loc)
  if(out_edges.indexOf(srcid) == -1)
    out_edges.push(srcid);
}

function var_iter(f, v) {
  f(v)
  for each(var va in v.assign)
    var_iter(f, va)
  for each(var vp in v.arguments)
    var_iter(f, vp)
}

function process(vars) {
  first = vars[0]
  if(first.isFunction) my_decl = shorten(first.loc)
  for each(var v in vars)
    var_iter(process_v, v)
}

function input_end() {
  write_file(CALLGRAPH_FILE, uneval(graph))
}

function add_overriding_entry(name, type, class, overriding_class) {
  // try to find a virtual func to override
  // if none is found, look in the base classes
  for each(m in class.members) {
    if(!(m[MEM_NAME] == name && m[MEM_TYPE] == type)) continue
    var overrides = m[OVERRIDE_LS]
    if(overrides) {
      overrides.push(overriding_class)
    }
    // return even if we found a non-virtual one
    return true
  }
  for each(var b in class.bases) {
    if(add_overriding_entry(name, type, b, overriding_class))
      return true
  }
}

function process_class(c) {
  var obj = new Object()
  if(graph.inheritance[c.name]) return
  var bases = c.bases.map(function(x) {return graph.inheritance[x]}).filter(function(x) {if(x) return true})
  if(bases.length) {
    obj.bases = bases
  }
  obj.members = c.members.map(function (x) {
    var v = [x.name, add_item(graph.types, x.type), shorten(x.loc)]
    if(x.isVirtual)
        v[OVERRIDE_LS] = []
    return v
  })
    
  for each(var m in obj.members) {
    if(!m[OVERRIDE_LS])
      continue
    for each(var b in obj.bases) {
      if(add_overriding_entry(m[MEM_NAME], m[MEM_TYPE], b, obj))
        break
    }
  }
  graph.inheritance[c.name] = obj
}
