view runtime/object.c @ 186:ba35ab624ec2

Add support for raw C function output from C backend as well as an option to use Boehm-GC instead of reference counting
author Mike Pavone <pavone@retrodev.com>
date Fri, 07 Oct 2011 00:10:02 -0700
parents a68e6828d896
children
line wrap: on
line source

#include "object.h"
#include "builtin.h"
#ifdef USE_GC
#include <gc/gc.h>
#else
#include <stdlib.h>
#ifndef USE_MALLOC
#include "fixed_alloc.h"
#endif
#endif
#include <string.h>
#include <stdio.h>

blueprint ** registered_types = NULL;
uint32_t max_registered_type = 0;
uint32_t type_storage = 0;

mem_manager * manager = NULL;
/*

returntype call_method(uint32_t methodid, calldata * params)
{
	int i;
	blueprint * bp = get_blueprint(params->params[0]);
	if(methodid >= bp->first_methodid && methodid < bp->last_methodid && bp->method_lookup[methodid - bp->first_methodid])
	{
		params->tail_func =  bp->method_lookup[methodid - bp->first_methodid];
		return TAIL_RETURN;
	} else {
		if(METHOD_MISSING >= bp->first_methodid && METHOD_MISSING < bp->last_methodid && bp->method_lookup[METHOD_MISSING - bp->first_methodid])
		{
			params->tail_func = bp->method_lookup[METHOD_MISSING - bp->first_methodid];
			return TAIL_RETURN;
		} else {
			//TODO: Add useful info to exception
			for(i = 0; i < params->num_params; ++i)
				release_ref(params->params[i]);
			params->params[0] = new_object(TYPE_METHODMISSINGEXCEPTION);
			return EXCEPTION_RETURN;
		}
	}
}

returntype set_field(uint32_t setfieldid, calldata * params)
{
	int i;
	blueprint * bp = get_blueprint(params->params[0]);
	if(setfieldid >= bp->first_setfieldid && setfieldid < bp->last_setfieldid && bp->setter_lookup[setfieldid - bp->first_setfieldid])
	{
		params->tail_func =  bp->setter_lookup[setfieldid - bp->first_setfieldid];
		return TAIL_RETURN;
	} else {
		if(METHOD_SETFIELDMISSING >= bp->first_setfieldid && METHOD_SETFIELDMISSING < bp->last_setfieldid && bp->method_lookup[METHOD_SETFIELDMISSING - bp->first_methodid])
		{
			params->tail_func = bp->method_lookup[METHOD_SETFIELDMISSING - bp->first_methodid];
			params->original_methodid = setfieldid;
			return TAIL_RETURN;
		} else {
			//TODO: Add useful info to exception
			for(i = 0; i < params->num_params; ++i)
				release_ref(params->params[i]);
			params->params[0] = new_object(TYPE_FIELDMISSINGEXCEPTION);
			return EXCEPTION_RETURN;
		}
	}
}


returntype get_field(uint32_t getfieldid, calldata * params)
{
	int i;
	blueprint * bp = get_blueprint(params->params[0]);
	if(getfieldid >= bp->first_getfieldid && getfieldid < bp->last_getfieldid && bp->getter_lookup[getfieldid - bp->first_getfieldid])
	{
		params->tail_func =  bp->getter_lookup[getfieldid - bp->first_getfieldid];
		return TAIL_RETURN;
	} else {
		if(METHOD_GETFIELDMISSING >= bp->first_getfieldid && METHOD_GETFIELDMISSING < bp->last_getfieldid && bp->method_lookup[METHOD_GETFIELDMISSING - bp->first_methodid])
		{
			params->tail_func = bp->method_lookup[METHOD_GETFIELDMISSING - bp->first_methodid];
			params->original_methodid = getfieldid;
			return TAIL_RETURN;
		} else {
			//TODO: Add useful info to exception
			for(i = 0; i < params->num_params; ++i)
				release_ref(params->params[i]);
			params->params[0] = new_object(TYPE_FIELDMISSINGEXCEPTION);
			return EXCEPTION_RETURN;
		}
	}
}

returntype convert_to(uint32_t convertto, calldata * params)
{
	int i;
	blueprint * bp = get_blueprint(params->params[0]);
	if(convertto >= bp->first_convertto && convertto < bp->last_convertto && bp->convert_to[convertto])
	{
		params->tail_func =  bp->convert_to[convertto - bp->first_convertto];
		return TAIL_RETURN;
	} else {
		return NO_CONVERSION;
	}
}

returntype convert_from(uint32_t convertfrom, calldata * params)
{
	int i;
	blueprint * bp = registered_types[convertfrom];
	if(convertfrom >= bp->first_convertfrom && convertfrom < bp->last_convertfrom && bp->convert_from[convertfrom])
	{
		params->tail_func =  bp->convert_from[convertfrom - bp->first_convertfrom];
		return TAIL_RETURN;
	} else {
		return NO_CONVERSION;
	}
}

returntype coerce_value(uint32_t type, calldata * params)
{
	int i;
	blueprint * bp = get_blueprint(params->params[0]);
	if(bp == registered_types[type])
		return NORMAL_RETURN;
	if(convert_to(type, params) == TAIL_RETURN)
		return TAIL_RETURN;
	if(convert_from(type, params) == TAIL_RETURN)
		return TAIL_RETURN;
	//TODO: Add useful info to exception
	for(i = 0; i < params->num_params; ++i)
		release_ref(params->params[i]);
	params->params[0] = new_object(TYPE_WRONGTYPEEXCEPTION);
	return EXCEPTION_RETURN;
}*/

object * alloc_object(blueprint * bp)
{
#ifdef USE_GC
	return GC_malloc(bp->boxed_size);
#else
#ifdef USE_MALLOC
	return malloc(bp->boxed_size);
#else
	return falloc(bp->boxed_size, manager);
#endif
#endif
}

#ifndef USE_GC
void dealloc_object(blueprint * bp, object * obj)
{
#ifdef USE_MALLOC
	free(obj);
#else
	ffree(obj, bp->boxed_size, manager);
#endif
}
#endif

object * new_object(uint32_t type)
{
	blueprint * bp;
	object * ret;
	if(type >= max_registered_type || !registered_types[type])
		return NULL;
	bp = registered_types[type];
	return new_object_bp(bp);
}

object * new_object_bp(blueprint * bp)
{
	object * ret;
	ret = alloc_object(bp);
	if(ret)
	{
		ret->bprint = bp;
#ifndef USE_GC
		rh_atomic_set(ret, refcount, 1);
#endif
		memset(((char *)ret) + sizeof(object), '\0', bp->size);
		bp->init(ret);
	} else {
		fprintf(stderr, "Could not allocate new object with size %d\n", bp->boxed_size);
	}
	return ret;
}

multisize * new_multisize(uint32_t type, uint32_t size)
{
	blueprint *bp;
	multisize * ret;
	if(type >= max_registered_type || !registered_types[type])
		return NULL;
#ifdef USE_GC
	ret = GC_malloc(sizeof(multisize) + size);
#else
#ifdef USE_MALLOC	
	ret = malloc(sizeof(multisize) + size);
#else
	ret = falloc(sizeof(multisize) + size, manager);
#endif
#endif
	if(ret)
	{
		bp = registered_types[type];
		ret->base.bprint = bp;
		ret->size = size;
#ifndef USE_GC
		rh_atomic_set(&(ret->base), refcount, 1);
#endif
		memset(((char *)ret) + sizeof(multisize), '\0', size);
		bp->init((object *)ret);
	}
	return ret; 
}

object * copy_object(object * tocopy)
{
	object * copy;
	multisize * mcopy, *mtocopy;
	blueprint * bp;
#ifndef USE_GC
	if(rh_atomic_get(tocopy, refcount) == 1)
		return tocopy;
#endif
	bp = get_blueprint(tocopy);
	if(bp->size < 0) {
		mtocopy = (multisize *)tocopy;
#ifdef USE_GC
		mcopy = GC_malloc(sizeof(multisize) + mtocopy->size);
#else
#ifdef USE_MALLOC
		mcopy = malloc(sizeof(multisize) + mtocopy->size);
#else
		mcopy = falloc(sizeof(multisize) + mtocopy->size, manager);
#endif
#endif
		mcopy->size = mtocopy->size;
		memcpy(((char *)mcopy)+sizeof(multisize), ((char *)mtocopy)+sizeof(multisize), mtocopy->size);
		copy = (object *)mcopy;
	} else {
		copy = alloc_object(bp);
		memcpy(((char *)copy) + sizeof(object), ((char *)tocopy)+sizeof(object), bp->size);
	}
	copy->bprint = bp;
#ifndef USE_GC
	rh_atomic_set(copy, refcount, 1);
#endif
	bp->copy(copy);
	release_ref(tocopy);
	return copy;
}

object * naked_to_boxed(uint32_t type, void * rawdata)
{
	object * dest;
	blueprint * bp = get_blueprint_byid(type);
	if(!bp->boxed_size)
		return NULL; //We don't know how big a naked multi-size object is so we can't do anything with it
	dest = alloc_object(bp);
	memcpy(((char *)dest) + sizeof(object), rawdata, bp->size);
	dest->bprint = bp;
#ifndef USE_GC
	rh_atomic_set(dest, refcount, 1);
#endif
	bp->copy(dest);
	return dest;
}

void boxed_to_naked(object * src, void * dest)
{
	blueprint * bp = get_blueprint(src);
	if(!bp->boxed_size)
		return; //We don't know how big a naked multi-size object is so we can't do anything with it
	memcpy(dest, ((char *)src) + sizeof(object), bp->size);
	bp->copy(src);
}

#ifndef USE_GC
void release_ref(object * obj)
{
	if(rh_atomic_sub_testzero(obj, refcount, 1))
		get_blueprint(obj)->free(obj);
}
#endif

void check_type_storage(type)
{
	uint32_t type_storage_temp;
	blueprint ** temp;
	if(type >= type_storage)
		if(type_storage)
		{
			type_storage_temp = (type + (type_storage >> 1));
#ifdef USE_GC
			temp = GC_realloc(registered_types, type_storage_temp * sizeof(blueprint *));
#else
			temp = realloc(registered_types, type_storage_temp * sizeof(blueprint *));
#endif
			if(temp)
			{
				registered_types = temp;
				memset(registered_types + type_storage, '\0', (type_storage_temp - type_storage) * sizeof(blueprint *));
				type_storage = type_storage_temp;
			}
			else
			{
#ifndef USE_GC
				free(registered_types);
#endif
				fprintf(stderr, "Couldn't allocate %d bytes for type storage array\n", type_storage_temp * sizeof(blueprint *));
				exit(-1);
			}
		} else {
			
			if(type < INITIAL_TYPE_STORAGE)
				type_storage =INITIAL_TYPE_STORAGE;
			else
				type_storage = type + 8;
#ifdef USE_GC
			registered_types = GC_malloc(type_storage * sizeof(blueprint *));
#else
			registered_types = malloc(type_storage * sizeof(blueprint *));
#endif

			if(registered_types)
				memset(registered_types, '\0', type_storage * sizeof(blueprint *));
			else
			{
				fprintf(stderr, "Couldn't allocate %d bytes for type storage array\n", type_storage * sizeof(blueprint *));
				exit(-1);
			}
				
		}
}

void default_action(object * obj)
{
}

void normal_free(object * obj)
{
	blueprint * bp = get_blueprint(obj);
	if(bp->cleanup)
		bp->cleanup(obj);
#ifndef USE_GC
	ffree(obj, bp->boxed_size, manager);
#endif
}

void multi_free(object * obj)
{
	multisize * multi = (multisize *)obj;
	blueprint * bp = get_blueprint(obj);
	if(bp->cleanup)
		bp->cleanup(obj);
#ifndef USE_GC
	ffree(multi, sizeof(multisize) + multi->size, manager);
#endif
}

blueprint * new_blueprint(uint32_t type, int32_t size, special_func init, special_func copy, special_func cleanup)
{
#ifdef USE_GC
	blueprint * bp = GC_malloc(sizeof(blueprint));
#else
	blueprint * bp = malloc(sizeof(blueprint));

	//dirty hack!, move elsewhere
	if (!manager) {
		fixed_alloc_init();
		manager = new_mem_manager();
	}
#endif
	if(bp)
	{
		bp->size = size;
		bp->boxed_size = size >= 0 ? size + sizeof(object) : 0;
		bp->method_lookup = bp->getter_lookup = bp->setter_lookup = bp->convert_to = bp->convert_from = NULL;
		bp->type_id = type;
		bp->init = init ? init : default_action;
		bp->copy = copy ? copy : default_action;
		bp->cleanup = cleanup ? cleanup : default_action;
		bp->free = size >= 0 ? normal_free : multi_free;
		bp->first_methodid = bp->last_methodid = bp->first_getfieldid = bp->last_getfieldid = bp->first_setfieldid = bp->last_setfieldid = bp->first_convertto = bp->last_convertto = bp->first_convertfrom = bp->last_convertfrom = 0;
		//TODO: Handle names
		bp->name = NULL;
	}
	return bp;
}

blueprint * register_type_byid(uint32_t type, int32_t size, special_func init, special_func copy, special_func cleanup)
{
	check_type_storage(type);
	if(registered_types[type])
		return registered_types[type];
	registered_types[type] = new_blueprint(type, size, init, copy, cleanup);
	if(!registered_types[type])
	{
		fputs("Couldn't allocate new object blueprint\n", stderr);
		exit(-1);
	}
	if(type >= max_registered_type)
		max_registered_type = type + 1;
	return registered_types[type];
}

blueprint * register_type(int32_t size, special_func init, special_func copy, special_func cleanup)
{
	return register_type_byid(max_registered_type, size, init, copy, cleanup);
}

/*
void add_method(blueprint * bp, uint32_t methodid, rhope_func impl)
{
	rhope_func * temp;
	if(methodid < 1) {
		fputs("Attempt to add a method with an ID < 1\n", stderr);
		exit(-1);
	}
	if (!bp->method_lookup)
	{
		bp->method_lookup = malloc(sizeof(rhope_func) * INITIAL_METHOD_LOOKUP);
		if(!bp->method_lookup) {
			fprintf(stderr, "Couldn't allocate %d bytes for method lookup table\n", sizeof(rhope_func) * INITIAL_METHOD_LOOKUP);
			exit(-1);
		}
		if(BELOW_INITIAL_METHOD > methodid) {
			bp->first_methodid = 1;
			bp->last_methodid = 1+INITIAL_METHOD_LOOKUP;
		} else {
			bp->first_methodid = methodid - BELOW_INITIAL_METHOD;
			bp->last_methodid = bp->first_methodid + INITIAL_METHOD_LOOKUP;
		}
		memset(bp->method_lookup, '\0', sizeof(rhope_func) * INITIAL_METHOD_LOOKUP);
	} else {
		if (methodid < bp->first_methodid) {
			temp = bp->method_lookup;
			//Note: if this gets changed to generating an exception on failure, we need to restore the original buffer
			bp->method_lookup = malloc(sizeof(rhope_func) * (bp->last_methodid-methodid));
			if(!bp->method_lookup) {
				fprintf(stderr, "Couldn't allocate %d bytes for method lookup table\n", sizeof(rhope_func) * (bp->last_methodid-methodid));
				exit(-1);
			}
			memset(bp->method_lookup, '\0', (bp->first_methodid-methodid) * sizeof(rhope_func));
			memcpy(bp->method_lookup + bp->first_methodid-methodid, temp, (bp->last_methodid-bp->first_methodid)*sizeof(rhope_func));
			free(temp);
			bp->first_methodid = methodid;
		} else if(methodid >= bp->last_methodid) {
			//Note: if this gets changed to generating an exception on failure, we need to restore the original buffer
			bp->method_lookup = realloc(bp->method_lookup, (methodid+1-bp->first_methodid) * sizeof(rhope_func));
			if(!bp->method_lookup) {
				fprintf(stderr, "Couldn't resize method lookup table to %d bytes\n", (methodid+1-bp->first_methodid) * sizeof(rhope_func));
				exit(-1);
			}
			memset(bp->method_lookup+bp->last_methodid, '\0', (methodid+1)-bp->last_methodid);
			bp->last_methodid = methodid+1;
		}
	}
	bp->method_lookup[methodid-bp->first_methodid] = impl;
}

void add_getter(blueprint * bp, uint32_t getfieldid, rhope_func impl)
{
	rhope_func * temp;
	if(getfieldid < 1) {
		fputs("Attempt to add a method with an ID < 1\n", stderr);
		exit(-1);
	}
	if (!bp->getter_lookup)
	{
		bp->getter_lookup = malloc(sizeof(rhope_func) * INITIAL_METHOD_LOOKUP);
		if(!bp->getter_lookup) {
			fprintf(stderr, "Couldn't allocate %d bytes for getter lookup table\n", sizeof(rhope_func) * INITIAL_METHOD_LOOKUP);
			exit(-1);
		}
		if(BELOW_INITIAL_METHOD > getfieldid) {
			bp->first_getfieldid = 1;
			bp->last_getfieldid = 1+INITIAL_METHOD_LOOKUP;
		} else {
			bp->first_getfieldid = getfieldid - BELOW_INITIAL_METHOD;
			bp->last_getfieldid = bp->first_getfieldid + INITIAL_METHOD_LOOKUP;
		}
		memset(bp->getter_lookup, '\0', sizeof(rhope_func) * INITIAL_METHOD_LOOKUP);
	} else {
		if (getfieldid < bp->first_getfieldid) {
			temp = bp->getter_lookup;
			//Note: if this gets changed to generating an exception on failure, we need to restore the original buffer
			bp->getter_lookup = malloc(sizeof(rhope_func) * (bp->last_getfieldid-getfieldid));
			if(!bp->getter_lookup) {
				fprintf(stderr, "Couldn't allocate %d bytes for getter lookup table\n", sizeof(rhope_func) * (bp->last_getfieldid-getfieldid));
				exit(-1);
			}
			memset(bp->getter_lookup, '\0', (bp->first_getfieldid-getfieldid) * sizeof(rhope_func));
			memcpy(bp->getter_lookup + bp->first_getfieldid-getfieldid, temp, (bp->last_getfieldid-bp->first_getfieldid)*sizeof(rhope_func));
			free(temp);
			bp->first_getfieldid = getfieldid;
		} else if(getfieldid >= bp->last_getfieldid) {
			//Note: if this gets changed to generating an exception on failure, we need to restore the original buffer
			bp->getter_lookup = realloc(bp->getter_lookup, (getfieldid+1-bp->first_getfieldid) * sizeof(rhope_func));
			if(!bp->getter_lookup) {
				fprintf(stderr, "Couldn't resize getter lookup table to %d bytes\n", (getfieldid+1-bp->first_getfieldid) * sizeof(rhope_func));
				exit(-1);
			}
			memset(bp->getter_lookup+bp->last_getfieldid, '\0', (getfieldid+1)-bp->last_getfieldid);
			bp->last_getfieldid = getfieldid+1;
		}
	}
	bp->getter_lookup[getfieldid-bp->first_getfieldid] = impl;
}

void add_setter(blueprint * bp, uint32_t setfieldid, rhope_func impl)
{
	rhope_func * temp;
	if(setfieldid < 1) {
		fputs("Attempt to add a method with an ID < 1\n", stderr);
		exit(-1);
	}
	if (!bp->setter_lookup)
	{
		bp->setter_lookup = malloc(sizeof(rhope_func) * INITIAL_METHOD_LOOKUP);
		if(!bp->setter_lookup) {
			fprintf(stderr, "Couldn't allocate %d bytes for setter lookup table\n", sizeof(rhope_func) * INITIAL_METHOD_LOOKUP);
			exit(-1);
		}
		if(BELOW_INITIAL_METHOD > setfieldid) {
			bp->first_setfieldid = 1;
			bp->last_setfieldid = 1+INITIAL_METHOD_LOOKUP;
		} else {
			bp->first_setfieldid = setfieldid - BELOW_INITIAL_METHOD;
			bp->last_setfieldid = bp->first_setfieldid + INITIAL_METHOD_LOOKUP;
		}
		memset(bp->setter_lookup, '\0', sizeof(rhope_func) * INITIAL_METHOD_LOOKUP);
	} else {
		if (setfieldid < bp->first_setfieldid) {
			temp = bp->setter_lookup;
			//Note: if this sets changed to generating an exception on failure, we need to restore the original buffer
			bp->setter_lookup = malloc(sizeof(rhope_func) * (bp->last_setfieldid-setfieldid));
			if(!bp->setter_lookup) {
				fprintf(stderr, "Couldn't allocate %d bytes for setter lookup table\n", sizeof(rhope_func) * (bp->last_setfieldid-setfieldid));
				exit(-1);
			}
			memset(bp->setter_lookup, '\0', (bp->first_setfieldid-setfieldid) * sizeof(rhope_func));
			memcpy(bp->setter_lookup + bp->first_setfieldid-setfieldid, temp, (bp->last_setfieldid-bp->first_setfieldid)*sizeof(rhope_func));
			free(temp);
			bp->first_setfieldid = setfieldid;
		} else if(setfieldid >= bp->last_setfieldid) {
			//Note: if this sets changed to generating an exception on failure, we need to restore the original buffer
			bp->setter_lookup = realloc(bp->setter_lookup, (setfieldid+1-bp->first_setfieldid) * sizeof(rhope_func));
			if(!bp->setter_lookup) {
				fprintf(stderr, "Couldn't resize setter lookup table to %d bytes\n", (setfieldid+1-bp->first_setfieldid) * sizeof(rhope_func));
				exit(-1);
			}
			memset(bp->setter_lookup+bp->last_setfieldid, '\0', (setfieldid+1)-bp->last_setfieldid);
			bp->last_setfieldid = setfieldid+1;
		}
	}
	bp->setter_lookup[setfieldid-bp->first_setfieldid] = impl;
}
*/

blueprint * get_blueprint_byid(uint32_t type)
{
	if(type >= max_registered_type)
		return NULL;
	return registered_types[type];
}