eval(read_file("dehydra_scripts/query_inheritance.js"))

function getParentWithMember(className, memberName) {
  var c = getCachedClass(className)
  var members = c.getMembers(memberName)
  if (!members || members.length == 0) {
    throw("No match for " + className + "::" + memberName)
  } else if (members.length != 1) {
    throw("Giving up due to multiple overloads for " + className + "::" + memberName)
  }
  var member = members[0]
  var matches = [c]
  var parent = c
  if (member.isVirtual) {
    parent = c.getParentWithMember(member)
    if (parent.isXPIDL())
      return undefined
    var children = parent.getChildren()
    children.push(parent)
    // filter out children that don't have this method imlemented
    matches = children.filter(function (c) {
      var m = c.getMember(memberName)
      return m && !m.isPureVirtual()
    })
  }
  if (matches.length == 1) return parent
}

const nsresult = "unsigned int"
var states = []

function process(vars, state) {
  var f = vars[0];
  if(f.isFunction && f.type == nsresult) {
    delete vars[0]
    for each (var v in vars) {
      var m = /(\w+) (\*\*)/(v.type);
      if(!v.isArg || !m)
        continue;

      var index = vars.indexOf(v) - 1;
      var classtype = ""
      if(vars[1].name == "__receiver") {
        index--
        classtype = /([^ ]+)( const)? &/(vars[1].type)[1]
      } else return;
      var params = f.parameters.map(function (x) {return x.type}).join(",")
      return {name:classtype+"::"+f.name, aliased:[], decl:f.loc,
              outparam:v.name, outparam_index:index, assigns:[], errors:{},
              params:params};
      }
  } 
  if (!state)
    return;
  
  function process_v(v) {
    if(v.isReturn) {
      state.ret = v;
      return;
    }
    if (!v.assign) {
      if(v.isAlias && v.type == nsresult)
        state.aliased.push(v.name)
      return;
    }
    if (v.name == state.outparam) {
      state.assigns.push(v.assign[0]);
      return;
    } else if (v.type != nsresult || is_global(v.id))
      return;
    var e = state.errors[v.name];
    var from = v.assign[0];
    if (!e) 
      state.errors[v.name] = {from:[from], val:from};
    else {
      e.from.push(from);
      e.val = from;
    }
    //print(v.name + "="+v.assign)
  }
  iter(process_v, vars);
  return state;
}

function path_end(state) {
  if(state)
    states.push(state);
}

function graph_end() {
  var my_states = states;
  states = [];
  var rets = [];
  var retVals = [];
  var assigns = [];
  function add_unique(ls, x) {
    for each (var i in ls) {
      if (structurally_equal(i,x)) 
        return;
    }
    ls.push(x)
  }

  for each (var state in my_states) {
    var curret = state.ret;
    if(!curret) {
      return;
    }
    if (curret.name) {
      if(curret.fieldOf || curret.isFcall 
         || state.aliased.indexOf(state.ret.name) != -1 
         || is_global(curret.id)) {
        return;
      }
      //print(is_global(state.ret.id)+" "+state.aliased+" "+state.ret)
      var err =  state.errors[curret.name]
      if (!err) return
      add_unique(retVals, err.val);
      add_unique(rets, curret);
    }
    else
      add_unique(retVals, curret);
    for each (var a in state.assigns) {
      add_unique(assigns, a);
    }
  }
  var haveNS_OK;
  for each (var ret in retVals) {
    if(ret.id)
      return;
    if(ret.value == 0)
      haveNS_OK = true;
  }
  if(!haveNS_OK || retVals.length > 2)
    return;
  if(!assigns.length)
    return;
  var rval = rets.length == 1 ? rets[0].name : "";
  
  // uncute work-around..assume if returning NULL & non-null, NULL is returned on error
  /*var filtered = assigns.filter(function (x) {if (typeof x == "undefined") print (x);return !(x.value == "0")});
  if (filtered.length == 1)
    assigns = filtered

  if (assigns.length > 1)
    print("Many assigns in this one "+assigns)

  var assign = assigns[0];
  if (assign.name)
    assign = assign.name
  else if (assign.value)
    assign = assign.value
  */
  var name = my_states[0].name
  split = name.split("::")
  if (split.length > 1) {
    try {
      var top = getParentWithMember(split[0], split[1])
      // only flat methods that have a single implementation
      if (!top)
        return
      name = top.name + "::" + split[1]
    } catch(e) {
      print(name + ": " + e)
      return
    }
  }
  print("OUTPARAMS:"+name+"("+my_states[0].params+")" + ","+my_states[0].outparam_index)
}
