//JavaScript GC on GC
//algorithm is "mark-and-sweep"
//someday challenging "incremental" or "concurrent" gc
//.. when is someday?

var gc = {

  heap : [],

  heap_size : 100,

  starvation_p : 0.1,

  free_list : [],

  main : function() {
    gc.add_heap();
    gc.garbage_collect();
  },

  new_chunk : function(){
    return { obj : null, color : 0, is_gc_obj : true, is_use : false };
  },

  garbage_collect : function() {
    gc.mark();
    gc.sweep();
    setTimeout(arguments.callee, 10000);
  },

  add_heap : function(){
    var pt=0;

    if(!gc.heap.length){
      gc.heap = new Array(gc.heap_size);
    } else {
      pt = gc.heap.length;
      gc.heap = gc.heap.concat(new Array(Math.floor(gc.heap.length*0.2)));
    }

    for(var i=pt,len=gc.heap.length; i<len; i++) {
      gc.free_list.push(i);
      gc.heap[i] = gc.new_chunk();
    }
  },

  mark_color : {black : 1, white : 0},

  mark : function() {
    gc.mark_child(window);
  },

  mark_child : function(obj) {
    if(!obj) return;
    if(obj.is_gc_obj) obj.color = gc.mark_color.black;
    for(var e in obj) {
      // darty hack
      try { 
        if(
          e &&
          obj[e] &&
          !{self : true, document : true, location : true, navigator : true,
            console : true, Components : true, controllers : true,
            screen : true, scrollbars : true, __firebug__ : true,
            netscape : true, userVars : true, history : true,
            gc_watcher : true,
            crypto : true, pkcs11 : true, gc : true}[e] &&
          !obj[e].document && 
          !obj[e].visible && 
          "object" == typeof(obj[e])
        ) {
          gc.mark_child(obj[e]);
        }
      } catch(e) {}
    }
  },

  sweep : function() {
    gc.free_list = [];

    for(var i=0,len=gc.heap.length, h=gc.heap; i<len; i++) {
      if(!h[i].is_use) gc.free_list.push(i);

      // free (please garbage collect to javascript gc)
      if(h[i].is_use && h[i].color == gc.mark_color.white) {
        h[i].obj = null;
        h[i].is_use = false;
        gc.free_list.push(i);
        continue;
      }

      //mark reset
      h[i].color = gc.mark_color.white;
    }
  },

  new_obj : function() {
    if(gc.free_list.length <= gc.heap.length*gc.starvation_p) gc.main();
    var obj = gc.heap[gc.free_list.shift()];
    obj.is_use = true;
    return obj;
  }

};

