obj.c 8.71 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338

#include "obj.h"
#include "dynar.h"


/* Beoran's Objects, Objective-C/Smalltalk/Javascript-ish */
struct BObject_;
typedef struct BObject_ BObject;

/* A Class is simply an object too. */
typedef struct BObject_ BClass;


struct BMethod_;
typedef struct BMethod_ BMethod;

struct BMethodTable_;
typedef struct BMethodTable_ BMethodTable;

struct BInstanceVariable_;
typedef struct BInstanceVariable_ BInstanceVariable;

struct BInstanceVariableTable_;
typedef struct BInstanceVariableTable_ BInstanceVariableTable;


typedef BObject * BMethodFunction(BObject * self, int argc, BObject * argv[]);

union BValue_ {
  BObject * bobject;
  int       integer;
  double    number;
  void    * pointer;
  char    * cstr;
};


struct BMethod_ {
  char            * name;
  BMethodFunction * action; 
};

struct BInstanceVariable_ {
  char            * name;
  void            * value; 
};

struct BMethodTable_ {
  BMethod * last;
  BMethod * methods;
  int size;
};

struct BInstanceVariableTable_ {
  BInstanceVariable * last;
  Dynar             * variables;
  int size;
};



struct BObject_ {
  BMethodTable  methods_;
  BObject     * prototype_;
};



BObject * bobject_init(BObject * self) {
  return self;
}






/* 
  Ideas for refcount/interface combo system. 
  
  Interface contains only self, and a pointer to a method table. 
  Rationale: The method table could be static and reused 
  to save memory, or allocated at runtime and dynamic when that's needed. 
  If we were to store the methods diirectly in the interface, this would 
  take up a lot of duplicate space for every same struct wrapped in an 
  interface.  
  The method table is simply a struct of function pointers. However,
  the first pointers must be a free() function.
  
  Finally, the Ref wraps an interface and a refcount.
  
  The first two methods are always 
  Reference contains refcount and interface to object. 

*/


struct Metab_ {
  void * (*free)(void * ptr);
};

struct Inter_ {
  void         * data;
  struct Metab * methods;
};


/** Ref is a reference counted wrapper around arbitrary data pointer, */
struct Ref_ {
  struct Inter_ * inter;
  int             refcount;
};

/*
Ref * ref_init(Ref * ref, Inter * inter) {
  return NULL;
} 
*/


/* Hidden object header that implements the reference counting. */
struct ObjHeader_ {
  struct ObjClass_  * klass;
  int                 refcount;
};

typedef struct ObjHeader_ ObjHeader;


struct ObjPoolNode_ {
  // linked list to next object in the pool
  ObjPoolNode       * next;
  // object registered with the pool
  void *            * data; 
};


/** Initializes an object pool for use */
ObjPool * objpool_init(ObjPool * pool) {
  if(!pool) return NULL;
  pool->last = NULL;
  return pool;
}




/* Allocates a new object pool node for the given object header. */
static ObjPoolNode * objpoolnode_new(ObjPoolNode * next, void * data) {
  ObjPoolNode * self  = STRUCT_ALLOC(ObjPoolNode);
  if(!self) return NULL;
  self->next          = next;
  self->data          = data;
  return self;
}


/** Registers data to be used with the given pool. */
void * objpool_register_data(ObjPool * pool, void * data) {
  ObjPoolNode * node;
  if(!pool) return NULL;
  // the next in the linked list will be the last registered object of the pool 
  node = objpoolnode_new(pool->last, data);
  if(!node) return NULL;
  // now the last in the pool must be the registered object
  pool->last = node;
  return data;
}



/** Allocates an object with the given size in the given pool. 
This will work much like mem_alloc / calloc, except that 
a hidden header is placed before the result, 
which enables reference counting. If klass is NULL,
a default class will be used that does nothing if done is called
and that uses mem_free to free the memory. If pool is NULL, 
then this works just like obj_alloc. */
void * obj_alloc_in_pool(ObjPool* pool, size_t size, ObjClass * klass) {
  char * ptr                = mem_alloc(size + sizeof(ObjHeader));
  ObjHeader  * aid          = (ObjHeader *) ptr;
  if(!aid) return NULL;
  aid->klass                = klass;
  aid->refcount             = 1;
  // Register the pool if we have one.
  if (pool) {
    objpool_register_data(pool, aid);
  }
  return                    ptr + sizeof(ObjHeader);
}


/** Allocates an object with the given size. 
This will work much like mam_alloc / calloc, except that 
a hidden header is placed before the result, 
which enables reference counting. If klass is NULL,
a default class will be used that does nothing if done is called
and that uses mem_free to free the memory. */
void * obj_alloc(size_t size, ObjClass * klass) {
  return obj_alloc_in_pool(NULL, size, klass); 
}

/* Gets the hidden header for this pointer. Only works on pointers allocated 
with obj_alloc. */
static ObjHeader * obj_objheader(void * ptr) {
  ObjHeader * header;
  char * aid;
  if(!ptr) return NULL;
  aid     = (char *) ptr;
  header  = (ObjHeader *) (aid - sizeof(ObjHeader));
  return header;
}

/** This increases the reference count and returns 
the pointer passed in. Only works on pointer allocated 
with obj_alloc. Use this when you want to retain a reference 
to the object pointed to by ptr.*/
void * obj_ref(void * ptr) {
  ObjHeader * header;
  header = obj_objheader(ptr);
  if(!header) return NULL;
  header->refcount++;
  return ptr;  
}


/** This gets the class info for this object. 
May return null for unclassed objects. Only works on
objects allocated (directly or indirectly) through obj_alloc. */
ObjClass * obj_class(void * ptr) {
  ObjHeader * header = obj_objheader(ptr);
  if (!header) return NULL;
  return header->klass;
}

/** Looks up a function pointer in the class at the given offset. */
/** XXX: this approach is not ISO C. */
ObjMethod * objclass_method_at(ObjClass * klass, size_t offset) { 
  char * aid;
  if(!klass) return NULL;
  aid = (char *) klass;
  return (ObjMethod*) (aid + offset); 
}


/** Looks up the free method for the given class. */
ObjMethod * objclass_getfree(ObjClass * klass) {
  return objclass_method_at(klass, offsetof(ObjClass, free));
}


/** Calls the first step of the destructor (done) on the object
pointed to by ptr. */
void * obj_done(void * ptr) {
  ObjHeader * header;
  ObjClass  * info;
  // look for a destructor in the linked type info tables
  for (info = obj_class(ptr); info; info = info->up) {
    if(info->done) { // call done destructor if found.
      return info->done(ptr);
    }
  }
  // if we get here do nothing.
  return ptr;
}

/** Calls the second step of the destructor (free) on the object that must have
been allocated though obj_alloc and pointed to indirectly by ptr. If the object 
has no class or no free function, mem_free will be called to free the memory 
that ptr(indirectly) references. Give the class a do-nothing free function to 
avoid this if needed. Returns NULL, or whatever the free method
of the object's class returns. The idea is to call this as ptr = obj_free(ptr). 
*/
void * obj_free(void * ptr) {
  ObjHeader * header;
  ObjClass  * info;
  // look for a destructor in the linked type info tables
  for (info = obj_class(ptr); info; info = info->up) {
    if(info->free) { // free destructor if found.
      return info->free(ptr);
    }
  }
  // If we get here, call the default free. First get the header...
  header = obj_objheader(ptr);
  //... then deallocate through the header, since the pointer to the header 
  // points to the top of the memory allocated though obj_alloc
  // if the header is not found, try to call mem_free directly.
  if (header) mem_free(header); else mem_free(ptr);  
  return NULL;
}


/** Reduce the reference count of the object, possibly calling done and free 
on the object if it's reference count became 0. This will return NULL if 
the reference count became 0, otherwise it will return ptr. */
void * obj_unref(void * ptr) { 
  ObjHeader * header;
  header = obj_objheader(ptr);
  if(!header) return NULL;
  header->refcount--;
  /* only free at exactly 0, if unref is called too much, 
    it will go negative and the free will not be called too many times */
  if(header->refcount == 0) {
    // first call the destructor, then the free function.
    obj_done(ptr);
    obj_free(ptr); 
    return NULL;
  }
  return ptr;
}




/** Walks though the object pool, and calls unref on each object. 
If the object is effectively destroyed, it will be removed
from the pool too. Otherwise, it remains in the pool. */
ObjPool * objpool_unref(ObjPool * pool) {
  ObjPoolNode * node, * next, * prev;
  if(!pool) return NULL;
  prev = NULL;
  node = pool->last; 
  while (node) {
    if(!(obj_unref(node->data))) { 
    // not correct, need to change ObjPoolNode!!!
      next = node->next;
      if (!prev) { // begin of list
        pool->last = node->next;
      } else {
        prev->next = next;
      }
      mem_free(node);
      node = next;
    }
  }
  return pool;
}