obj.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. #include "obj.h"
  2. #include "dynar.h"
  3. /* Beoran's Objects, Objective-C/Smalltalk/Javascript-ish */
  4. struct BObject_;
  5. typedef struct BObject_ BObject;
  6. /* A Class is simply an object too. */
  7. typedef struct BObject_ BClass;
  8. struct BMethod_;
  9. typedef struct BMethod_ BMethod;
  10. struct BMethodTable_;
  11. typedef struct BMethodTable_ BMethodTable;
  12. struct BInstanceVariable_;
  13. typedef struct BInstanceVariable_ BInstanceVariable;
  14. struct BInstanceVariableTable_;
  15. typedef struct BInstanceVariableTable_ BInstanceVariableTable;
  16. typedef BObject * BMethodFunction(BObject * self, int argc, BObject * argv[]);
  17. union BValue_ {
  18. BObject * bobject;
  19. int integer;
  20. double number;
  21. void * pointer;
  22. char * cstr;
  23. };
  24. struct BMethod_ {
  25. char * name;
  26. BMethodFunction * action;
  27. };
  28. struct BInstanceVariable_ {
  29. char * name;
  30. void * value;
  31. };
  32. struct BMethodTable_ {
  33. BMethod * last;
  34. BMethod * methods;
  35. int size;
  36. };
  37. struct BInstanceVariableTable_ {
  38. BInstanceVariable * last;
  39. Dynar * variables;
  40. int size;
  41. };
  42. struct BObject_ {
  43. BMethodTable methods_;
  44. BObject * prototype_;
  45. };
  46. BObject * bobject_init(BObject * self) {
  47. return self;
  48. }
  49. /*
  50. Ideas for refcount/interface combo system.
  51. Interface contains only self, and a pointer to a method table.
  52. Rationale: The method table could be static and reused
  53. to save memory, or allocated at runtime and dynamic when that's needed.
  54. If we were to store the methods diirectly in the interface, this would
  55. take up a lot of duplicate space for every same struct wrapped in an
  56. interface.
  57. The method table is simply a struct of function pointers. However,
  58. the first pointers must be a free() function.
  59. Finally, the Ref wraps an interface and a refcount.
  60. The first two methods are always
  61. Reference contains refcount and interface to object.
  62. */
  63. struct Metab_ {
  64. void * (*free)(void * ptr);
  65. };
  66. struct Inter_ {
  67. void * data;
  68. struct Metab * methods;
  69. };
  70. /** Ref is a reference counted wrapper around arbitrary data pointer, */
  71. struct Ref_ {
  72. struct Inter_ * inter;
  73. int refcount;
  74. };
  75. /*
  76. Ref * ref_init(Ref * ref, Inter * inter) {
  77. return NULL;
  78. }
  79. */
  80. /* Hidden object header that implements the reference counting. */
  81. struct ObjHeader_ {
  82. struct ObjClass_ * klass;
  83. int refcount;
  84. };
  85. typedef struct ObjHeader_ ObjHeader;
  86. struct ObjPoolNode_ {
  87. // linked list to next object in the pool
  88. ObjPoolNode * next;
  89. // object registered with the pool
  90. void * * data;
  91. };
  92. /** Initializes an object pool for use */
  93. ObjPool * objpool_init(ObjPool * pool) {
  94. if(!pool) return NULL;
  95. pool->last = NULL;
  96. return pool;
  97. }
  98. /* Allocates a new object pool node for the given object header. */
  99. static ObjPoolNode * objpoolnode_new(ObjPoolNode * next, void * data) {
  100. ObjPoolNode * self = STRUCT_ALLOC(ObjPoolNode);
  101. if(!self) return NULL;
  102. self->next = next;
  103. self->data = data;
  104. return self;
  105. }
  106. /** Registers data to be used with the given pool. */
  107. void * objpool_register_data(ObjPool * pool, void * data) {
  108. ObjPoolNode * node;
  109. if(!pool) return NULL;
  110. // the next in the linked list will be the last registered object of the pool
  111. node = objpoolnode_new(pool->last, data);
  112. if(!node) return NULL;
  113. // now the last in the pool must be the registered object
  114. pool->last = node;
  115. return data;
  116. }
  117. /** Allocates an object with the given size in the given pool.
  118. This will work much like mem_alloc / calloc, except that
  119. a hidden header is placed before the result,
  120. which enables reference counting. If klass is NULL,
  121. a default class will be used that does nothing if done is called
  122. and that uses mem_free to free the memory. If pool is NULL,
  123. then this works just like obj_alloc. */
  124. void * obj_alloc_in_pool(ObjPool* pool, size_t size, ObjClass * klass) {
  125. char * ptr = mem_alloc(size + sizeof(ObjHeader));
  126. ObjHeader * aid = (ObjHeader *) ptr;
  127. if(!aid) return NULL;
  128. aid->klass = klass;
  129. aid->refcount = 1;
  130. // Register the pool if we have one.
  131. if (pool) {
  132. objpool_register_data(pool, aid);
  133. }
  134. return ptr + sizeof(ObjHeader);
  135. }
  136. /** Allocates an object with the given size.
  137. This will work much like mam_alloc / calloc, except that
  138. a hidden header is placed before the result,
  139. which enables reference counting. If klass is NULL,
  140. a default class will be used that does nothing if done is called
  141. and that uses mem_free to free the memory. */
  142. void * obj_alloc(size_t size, ObjClass * klass) {
  143. return obj_alloc_in_pool(NULL, size, klass);
  144. }
  145. /* Gets the hidden header for this pointer. Only works on pointers allocated
  146. with obj_alloc. */
  147. static ObjHeader * obj_objheader(void * ptr) {
  148. ObjHeader * header;
  149. char * aid;
  150. if(!ptr) return NULL;
  151. aid = (char *) ptr;
  152. header = (ObjHeader *) (aid - sizeof(ObjHeader));
  153. return header;
  154. }
  155. /** This increases the reference count and returns
  156. the pointer passed in. Only works on pointer allocated
  157. with obj_alloc. Use this when you want to retain a reference
  158. to the object pointed to by ptr.*/
  159. void * obj_ref(void * ptr) {
  160. ObjHeader * header;
  161. header = obj_objheader(ptr);
  162. if(!header) return NULL;
  163. header->refcount++;
  164. return ptr;
  165. }
  166. /** This gets the class info for this object.
  167. May return null for unclassed objects. Only works on
  168. objects allocated (directly or indirectly) through obj_alloc. */
  169. ObjClass * obj_class(void * ptr) {
  170. ObjHeader * header = obj_objheader(ptr);
  171. if (!header) return NULL;
  172. return header->klass;
  173. }
  174. /** Looks up a function pointer in the class at the given offset. */
  175. /** XXX: this approach is not ISO C. */
  176. ObjMethod * objclass_method_at(ObjClass * klass, size_t offset) {
  177. char * aid;
  178. if(!klass) return NULL;
  179. aid = (char *) klass;
  180. return (ObjMethod*) (aid + offset);
  181. }
  182. /** Looks up the free method for the given class. */
  183. ObjMethod * objclass_getfree(ObjClass * klass) {
  184. return objclass_method_at(klass, offsetof(ObjClass, free));
  185. }
  186. /** Calls the first step of the destructor (done) on the object
  187. pointed to by ptr. */
  188. void * obj_done(void * ptr) {
  189. ObjHeader * header;
  190. ObjClass * info;
  191. // look for a destructor in the linked type info tables
  192. for (info = obj_class(ptr); info; info = info->up) {
  193. if(info->done) { // call done destructor if found.
  194. return info->done(ptr);
  195. }
  196. }
  197. // if we get here do nothing.
  198. return ptr;
  199. }
  200. /** Calls the second step of the destructor (free) on the object that must have
  201. been allocated though obj_alloc and pointed to indirectly by ptr. If the object
  202. has no class or no free function, mem_free will be called to free the memory
  203. that ptr(indirectly) references. Give the class a do-nothing free function to
  204. avoid this if needed. Returns NULL, or whatever the free method
  205. of the object's class returns. The idea is to call this as ptr = obj_free(ptr).
  206. */
  207. void * obj_free(void * ptr) {
  208. ObjHeader * header;
  209. ObjClass * info;
  210. // look for a destructor in the linked type info tables
  211. for (info = obj_class(ptr); info; info = info->up) {
  212. if(info->free) { // free destructor if found.
  213. return info->free(ptr);
  214. }
  215. }
  216. // If we get here, call the default free. First get the header...
  217. header = obj_objheader(ptr);
  218. //... then deallocate through the header, since the pointer to the header
  219. // points to the top of the memory allocated though obj_alloc
  220. // if the header is not found, try to call mem_free directly.
  221. if (header) mem_free(header); else mem_free(ptr);
  222. return NULL;
  223. }
  224. /** Reduce the reference count of the object, possibly calling done and free
  225. on the object if it's reference count became 0. This will return NULL if
  226. the reference count became 0, otherwise it will return ptr. */
  227. void * obj_unref(void * ptr) {
  228. ObjHeader * header;
  229. header = obj_objheader(ptr);
  230. if(!header) return NULL;
  231. header->refcount--;
  232. /* only free at exactly 0, if unref is called too much,
  233. it will go negative and the free will not be called too many times */
  234. if(header->refcount == 0) {
  235. // first call the destructor, then the free function.
  236. obj_done(ptr);
  237. obj_free(ptr);
  238. return NULL;
  239. }
  240. return ptr;
  241. }
  242. /** Walks though the object pool, and calls unref on each object.
  243. If the object is effectively destroyed, it will be removed
  244. from the pool too. Otherwise, it remains in the pool. */
  245. ObjPool * objpool_unref(ObjPool * pool) {
  246. ObjPoolNode * node, * next, * prev;
  247. if(!pool) return NULL;
  248. prev = NULL;
  249. node = pool->last;
  250. while (node) {
  251. if(!(obj_unref(node->data))) {
  252. // not correct, need to change ObjPoolNode!!!
  253. next = node->next;
  254. if (!prev) { // begin of list
  255. pool->last = node->next;
  256. } else {
  257. prev->next = next;
  258. }
  259. mem_free(node);
  260. node = next;
  261. }
  262. }
  263. return pool;
  264. }