-
-
Save nilium/bcf9314d2395c2361008 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <stddef.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdarg.h> | |
#include <string.h> | |
typedef struct s_object object_t; | |
typedef struct s_class class_t; | |
typedef void (*cd_method_t)(object_t*); | |
typedef void (*method_t)(object_t*, ...); | |
/** junk that I put in to satisfy myself **/ | |
typedef struct s_method_info { | |
char *name; | |
char *signature; | |
method_t method; | |
} method_info_t; | |
typedef struct s_ivar_info { | |
ptrdiff_t offset; | |
char *name; | |
char *type; | |
} ivar_info_t; | |
/** this sort of counts **/ | |
struct s_class { | |
class_t *super; | |
char *name; | |
size_t instance_size; | |
cd_method_t initializer; | |
cd_method_t finalizer; | |
size_t num_methods; | |
method_info_t method_info[]; | |
size_t num_ivars; | |
ivar_info_t ivars_info[]; | |
}; | |
struct s_object { | |
class_t *clas; | |
uint32_t refs; | |
size_t size; // size of the ivars block | |
void *ivars; // <- THIS part counts: actual ivar data stored separate from the object | |
}; | |
// not that important | |
void class_instantiate__init(object_t *obj, class_t *clas, class_t *until) { | |
if (clas->super != until) | |
class_instantiate__init(obj, clas->super, until); | |
if (clas->initializer != NULL) | |
clas->initializer(obj); | |
} | |
// sort of important? | |
object_t *class_instantiate(class_t *clas) { | |
object_t *obj = (object_t*)malloc(sizeof(object_t)); | |
obj->clas = clas; | |
obj->refs = 1; | |
obj->ivars = (clas->instance_size == 0 ? NULL : (void*)malloc(clas->instance_size)); | |
class_instantiate__init(obj, clas, NULL); | |
return obj; | |
} | |
method_info_t *class_findMethod(class_t *clas, const char *name) { | |
while (clas != NULL) { | |
size_t index = 0; | |
for (; index < clas->num_methods; ++index) { | |
if (strcmp(clas->method_info[index].name, name) == 0) | |
return clas->method_info+index; | |
} | |
clas = clas->super; | |
} | |
return NULL; | |
} | |
ivar_info_t *class_findIvar(class_t *clas, const char *name) { | |
while (clas != NULL) { | |
size_t index = 0; | |
for (; index < clas->num_ivars; ++index) { | |
if (strcmp(clas->ivars_info[index].name, name) == 0) | |
return clas->ivars_info+index; | |
} | |
clas = clas->super; | |
} | |
return NULL; | |
} | |
// what I was getting at | |
// returns old class (newly introduced ivars are set to their default values) | |
class_t *object_setClass(object_t *obj, class_t *clas) { | |
if (obj->clas == clas) | |
return clas; | |
class_t *oldClass = obj->clas; | |
void *ivars = obj->ivars; | |
if (clas->instance_size == 0 && ivars != NULL) { | |
free(ivars); | |
ivars = NULL; | |
} else if (obj->size < clas->instance_size) { | |
ivars = (void*)realloc(ivars, clas->instance_size); | |
// realloc error | |
if (ivars == NULL) { | |
fprintf(stderr, "Error reallocating ivars for %s@%x while setting class to %s\n", | |
oldClass->name, (unsigned int)obj, clas->name); | |
exit(1); | |
} | |
obj->size = clas->instance_size; | |
} | |
obj->clas = clas; | |
obj->ivars = ivars; | |
for(; clas != NULL; clas = clas->super) { | |
class_t *superOld = oldClass; | |
for(; superOld != NULL && superOld != clas; superOld = superOld->super); | |
if (superOld != NULL) | |
break; | |
} | |
class_instantiate__init(obj, obj->clas, clas); | |
return oldClass; | |
} | |
object_t *object_retain(object_t *obj) { | |
if (obj != NULL) | |
++obj->refs; | |
return obj; | |
} | |
void object_destroy(object_t *obj) { | |
class_t *clas = obj->clas; | |
for (; clas != NULL; clas = clas->super) | |
if (clas->finalizer != NULL) clas->finalizer(obj); | |
if (obj->ivars != NULL) | |
free(obj->ivars); | |
free(obj); | |
} | |
void object_release(object_t *obj) { | |
if (obj == NULL) | |
return; | |
if (--obj->refs == 0) { | |
object_destroy(obj); | |
} | |
} | |
void object_getIvar(object_t *obj, const char *name, void **value) { | |
ivar_info_t *ivar = class_findIvar(obj->clas, name); | |
if (ivar == NULL) { | |
return; | |
} | |
switch (ivar->type[0]) { | |
case '@': {// object | |
*(object_t**)value = object_retain(*(object_t**)(obj->ivars+ivar->offset)); | |
} break; | |
case '*': // pointer | |
*value = *(void**)(obj->ivars+ivar->offset); | |
break; | |
case '#': // function pointer | |
*(method_t**)value = *(method_t**)(obj->ivars+ivar->offset); | |
break; | |
case '&': // value | |
switch(ivar->type[2]) { | |
case 'b': *(uint8_t*)value = *(uint8_t*)(obj->ivars+ivar->offset); break; | |
case 's': *(uint16_t*)value = *(uint16_t*)(obj->ivars+ivar->offset); break; | |
case 'i': *(uint32_t*)value = *(uint32_t*)(obj->ivars+ivar->offset); break; | |
case 'l': *(uint64_t*)value = *(uint64_t*)(obj->ivars+ivar->offset); break; | |
case 'f': *(float*)value = *(float*)(obj->ivars+ivar->offset); break; | |
case 'd': *(double*)value = *(double*)(obj->ivars+ivar->offset); break; | |
} | |
break; | |
default: exit(1); // TODO: I DON'T KNOW WHAT TO DO ANYMORE | |
} | |
} | |
void object_setIvar(object_t *obj, const char *name, ...) { | |
va_list args; | |
ivar_info_t *ivar = class_findIvar(obj->clas, name); | |
if (ivar == NULL) { | |
return; | |
} | |
va_start(args, name); | |
switch (ivar->type[0]) { | |
case '@': {// object | |
object_t *newvalue, *oldvalue, **pointer; | |
pointer = (object_t**)(obj->ivars+ivar->offset); | |
newvalue = va_arg(args, object_t*); | |
oldvalue = *pointer; | |
*pointer = object_retain(newvalue); | |
object_release(oldvalue); | |
} break; | |
case '*': // pointer | |
*(void**)(obj->ivars+ivar->offset) = va_arg(args, void*); | |
break; | |
case '#': // function pointer | |
*(method_t*)(obj->ivars+ivar->offset) = va_arg(args, method_t); | |
break; | |
case '&': // value | |
switch(ivar->type[2]) { | |
case 'b': *(uint8_t*)(obj->ivars+ivar->offset) = va_arg(args, int); break; | |
case 's': *(uint16_t*)(obj->ivars+ivar->offset) = va_arg(args, int); break; | |
case 'i': *(uint32_t*)(obj->ivars+ivar->offset) = va_arg(args, uint32_t); break; | |
case 'l': *(uint64_t*)(obj->ivars+ivar->offset) = va_arg(args, uint64_t); break; | |
case 'f': *(float*)(obj->ivars+ivar->offset) = (float)va_arg(args, double); break; | |
case 'd': *(double*)(obj->ivars+ivar->offset) = va_arg(args, double); break; | |
} | |
break; | |
default: exit(1); | |
} | |
va_end(args); | |
} | |
#define object_invokeMethod(object, name, args...)\ | |
class_findMethod((object)->clas, (name))->method((object), ## args) | |
// note: all of this compiles, but like any intelligent person would do, i did not see if it actually works (probably doesn't) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment