2003-11-29 04:40:25 +00:00
|
|
|
/*
|
|
|
|
object.c
|
|
|
|
|
|
|
|
Provides a primitive object framework to back objects
|
|
|
|
in higher level languages of QF so that they can share
|
|
|
|
objects. For example, Ruamoko and GIB would be able to
|
|
|
|
pass String objects to each other, even if the higher
|
|
|
|
-level implementations of String in each language differ.
|
|
|
|
|
|
|
|
Copyright (C) 2003 Brian Koropoff
|
|
|
|
|
|
|
|
Author: Brian Koropoff
|
|
|
|
Date: November 28, 2003
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to:
|
|
|
|
|
|
|
|
Free Software Foundation, Inc.
|
|
|
|
59 Temple Place - Suite 330
|
|
|
|
Boston, MA 02111-1307, USA
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2005-08-04 15:27:09 +00:00
|
|
|
static __attribute__ ((used)) const char rcsid[] =
|
2003-11-29 04:40:25 +00:00
|
|
|
"$Id$";
|
|
|
|
|
|
|
|
#ifdef HAVE_STRING_H
|
|
|
|
# include <string.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_STRINGS_H
|
|
|
|
# include <strings.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include "QF/dstring.h"
|
|
|
|
#include "QF/sys.h"
|
|
|
|
#include "QF/object.h"
|
|
|
|
#include "QF/va.h"
|
2003-12-07 22:45:32 +00:00
|
|
|
#include "garbage.h"
|
2003-12-07 04:50:46 +00:00
|
|
|
|
|
|
|
#include "QF/classes/ArrayList.h"
|
|
|
|
#include "QF/classes/Integer.h"
|
|
|
|
#include "QF/classes/Double.h"
|
2003-11-29 04:40:25 +00:00
|
|
|
|
2003-12-09 02:01:05 +00:00
|
|
|
Object *allObjs = NULL;
|
|
|
|
ArrayList *rootObj = NULL;
|
|
|
|
|
2003-12-09 06:57:35 +00:00
|
|
|
static qboolean
|
|
|
|
Object_Finalize_f (Object *self)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2003-11-29 04:40:25 +00:00
|
|
|
static String *
|
|
|
|
Object_ToString_f (Object *self)
|
|
|
|
{
|
2004-04-27 21:56:26 +00:00
|
|
|
return new(String, va("%s@%p", self->cl->name, self));
|
2003-11-29 04:40:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
Object_Init_f (Object *self)
|
|
|
|
{
|
2003-12-09 02:01:05 +00:00
|
|
|
self->allRefs = NULL;
|
2003-11-29 04:40:25 +00:00
|
|
|
self->toString = Object_ToString_f;
|
2003-12-09 06:57:35 +00:00
|
|
|
self->finalize = Object_Finalize_f;
|
2003-11-29 04:40:25 +00:00
|
|
|
Sys_DPrintf("%s@%p initing...\n", self->cl->name, self);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
Object_Deinit_f (Object *self)
|
|
|
|
{
|
|
|
|
Sys_DPrintf("%s@%p deiniting...\n", self->cl->name, self);
|
|
|
|
free (self);
|
|
|
|
}
|
|
|
|
|
|
|
|
static String *
|
|
|
|
Class_ToString_f (Object *self)
|
|
|
|
{
|
2004-04-27 21:56:26 +00:00
|
|
|
return new(String, CLASS(self)->name);
|
2003-11-29 04:40:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static Object *
|
|
|
|
Class_Init_f (Object *self, const char *name, unsigned int size, Class *parent, void *init, void *deinit, qboolean abstract)
|
|
|
|
{
|
|
|
|
superInit(Class, self);
|
|
|
|
CLASS(self)->name = strdup(name);
|
|
|
|
CLASS(self)->size = size;
|
|
|
|
CLASS(self)->parent = parent;
|
|
|
|
CLASS(self)->init = (Object_Init_t) init;
|
|
|
|
CLASS(self)->deinit = (Object_Deinit_t) deinit;
|
|
|
|
self->toString = Class_ToString_f;
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
Class_Deinit_f (Object *self) {
|
|
|
|
free ((void *)CLASS(self)->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object *
|
2004-04-27 21:56:26 +00:00
|
|
|
Object_Create (Class *cl, qboolean perm)
|
2003-11-29 04:40:25 +00:00
|
|
|
{
|
2004-04-27 21:56:26 +00:00
|
|
|
Object *new;
|
|
|
|
if (cl->abstract)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
new = calloc (1, cl->size);
|
2003-11-29 04:40:25 +00:00
|
|
|
new->cl = cl;
|
2003-12-09 02:01:05 +00:00
|
|
|
new->marked = false;
|
2003-12-09 06:57:35 +00:00
|
|
|
new->finalized = false;
|
2004-04-27 21:56:26 +00:00
|
|
|
if (perm || cl->alwaysperm) {
|
|
|
|
new->refs = 1;
|
|
|
|
new->nogc = true;
|
|
|
|
} else {
|
|
|
|
new->refs = 0;
|
|
|
|
new->nogc = false;
|
|
|
|
new->next = allObjs;
|
|
|
|
allObjs = new;
|
|
|
|
}
|
2003-11-29 04:40:25 +00:00
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Object_Delete (Object *obj)
|
|
|
|
{
|
|
|
|
Class *c;
|
|
|
|
for (c = obj->cl; c; c = c->parent)
|
|
|
|
c->deinit (obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
Object *
|
|
|
|
Object_Retain (Object *obj)
|
|
|
|
{
|
2003-12-07 22:45:32 +00:00
|
|
|
obj->refs++;
|
2003-11-29 04:40:25 +00:00
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
Object *
|
|
|
|
Object_Release (Object *obj)
|
|
|
|
{
|
2003-12-09 02:01:05 +00:00
|
|
|
if (obj->refs)
|
|
|
|
obj->refs--;
|
2004-04-27 21:56:26 +00:00
|
|
|
if (obj->nogc && !obj->refs) {
|
|
|
|
if (!obj->finalized) {
|
|
|
|
obj->finalized = true;
|
|
|
|
methodCall(obj, finalize);
|
|
|
|
}
|
|
|
|
if (!obj->refs) {
|
|
|
|
Object_Delete (obj);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2003-11-29 04:40:25 +00:00
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2003-11-29 23:09:50 +00:00
|
|
|
qboolean
|
|
|
|
Object_InstanceOf (Object *obj, Class *cl)
|
|
|
|
{
|
|
|
|
Class *c;
|
|
|
|
for (c = obj->cl; c; c = c->parent)
|
|
|
|
if (c == cl)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2003-12-09 02:01:05 +00:00
|
|
|
void
|
|
|
|
Object_AddToRoot (Object *obj)
|
|
|
|
{
|
|
|
|
methodCall(COLLECTION(rootObj), add, obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Object_RemoveFromRoot (Object *obj)
|
|
|
|
{
|
|
|
|
methodCall(COLLECTION(rootObj), remove, obj);
|
|
|
|
}
|
|
|
|
|
2003-11-29 04:40:25 +00:00
|
|
|
static void
|
2003-12-07 04:50:46 +00:00
|
|
|
Object_Test (void)
|
2003-11-29 04:40:25 +00:00
|
|
|
{
|
2003-12-07 04:50:46 +00:00
|
|
|
String *liststr;
|
2004-04-27 21:56:26 +00:00
|
|
|
Collection *list = new(ArrayList, classObj(Object), NULL);
|
2003-12-07 04:50:46 +00:00
|
|
|
|
2004-04-27 21:56:26 +00:00
|
|
|
methodCall(list, add, new(String, "Testing..."));
|
|
|
|
methodCall(list, add, new(String, "One"));
|
|
|
|
methodCall(list, add, new(Integer, 2));
|
|
|
|
methodCall(list, add, new(Double, 3.0));
|
2003-12-07 04:50:46 +00:00
|
|
|
|
|
|
|
liststr = methodCall(OBJECT(list), toString);
|
|
|
|
Sys_DPrintf("List: %s\n", liststr->str);
|
|
|
|
methodCall(LIST(list), removeAt, 2);
|
|
|
|
liststr = methodCall(OBJECT(list), toString);
|
|
|
|
Sys_DPrintf("List: %s\n", liststr->str);
|
2004-04-27 21:56:26 +00:00
|
|
|
methodCall(LIST(list), insertAt, 2, new(String, "Mr. Two!"));
|
2003-12-07 04:50:46 +00:00
|
|
|
liststr = methodCall(OBJECT(list), toString);
|
|
|
|
Sys_DPrintf("List: %s\n", liststr->str);
|
2003-12-09 02:01:05 +00:00
|
|
|
|
2004-04-27 21:56:26 +00:00
|
|
|
list = new(ArrayList, classObj(Object), NULL);
|
|
|
|
methodCall(list, add, new(String, "Don't free me!"));
|
|
|
|
methodCall(list, add, new(Integer, 5));
|
|
|
|
methodCall(list, add, new(Double, 3.14));
|
2003-12-09 02:01:05 +00:00
|
|
|
Object_AddToRoot (OBJECT(methodCall(list, iterator)));
|
2003-11-29 04:40:25 +00:00
|
|
|
}
|
|
|
|
|
2004-04-27 21:56:26 +00:00
|
|
|
Class * classObj(Object);
|
|
|
|
Class * classObj(Class);
|
2003-11-29 04:40:25 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
Object_Init (void)
|
|
|
|
{
|
|
|
|
/* There is somewhat of a chicken and egg problem
|
|
|
|
here.
|
|
|
|
*/
|
2004-04-27 21:56:26 +00:00
|
|
|
classObj(Object) = malloc (sizeof (Class));
|
|
|
|
classObj(Class) = malloc (sizeof (Class));
|
|
|
|
OBJECT(classObj(Object))->cl = Class_class;
|
|
|
|
OBJECT(classObj(Class))->cl = Class_class;
|
|
|
|
classObj(Class)->parent = classObj(Class);
|
|
|
|
classObj(Object)->init = (Object_Init_t) Object_Init_f;
|
|
|
|
classObj(Class)->alwaysperm = true;
|
|
|
|
OBJECT(classObj(Class))->nogc = OBJECT(classObj(Object))->nogc = true;
|
|
|
|
|
|
|
|
Class_Init_f (OBJECT(classObj(Object)), "Object", sizeof(Object), NULL, Object_Init_f, Object_Deinit_f, true);
|
|
|
|
Class_Init_f (OBJECT(classObj(Class)), "Class", sizeof(Class), classObj(Object), Class_Init_f, Class_Deinit_f, false);
|
|
|
|
retain(classObj(Object));
|
|
|
|
retain(classObj(Class));
|
2003-11-29 04:40:25 +00:00
|
|
|
/* Phew... Object and Class are now bootstrapped,
|
|
|
|
classes can now be created by instantiating
|
|
|
|
Class
|
|
|
|
*/
|
|
|
|
|
2003-12-07 04:50:46 +00:00
|
|
|
/* Initialize standard classes */
|
|
|
|
classInit(String);
|
|
|
|
classInit(Number);
|
|
|
|
classInit(Integer);
|
|
|
|
classInit(Double);
|
|
|
|
classInit(Iterator);
|
|
|
|
classInit(Collection);
|
|
|
|
classInit(List);
|
|
|
|
classInit(ArrayList);
|
|
|
|
|
2003-12-09 02:01:05 +00:00
|
|
|
rootObj = new(ArrayList, classObj(Object), NULL);
|
2003-11-29 04:40:25 +00:00
|
|
|
|
2003-12-07 04:50:46 +00:00
|
|
|
/* Run test */
|
2003-11-29 04:40:25 +00:00
|
|
|
Object_Test();
|
|
|
|
}
|
2003-12-07 22:45:32 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
Object_Garbage_Collect (void)
|
|
|
|
{
|
2003-12-09 02:01:05 +00:00
|
|
|
static unsigned int frames = 0;
|
|
|
|
|
|
|
|
frames++;
|
|
|
|
|
|
|
|
if (frames % 2000 == 0) {
|
2003-12-09 06:57:35 +00:00
|
|
|
Object *all, *last;
|
2003-12-09 02:01:05 +00:00
|
|
|
Sys_DPrintf("GC: Marking...\n");
|
|
|
|
Garbage_Do_Mark (OBJECT(rootObj));
|
|
|
|
Sys_DPrintf("GC: Sweeping...\n");
|
2003-12-09 06:57:35 +00:00
|
|
|
all = allObjs;
|
|
|
|
allObjs = NULL;
|
|
|
|
last = Garbage_Do_Sweep (&all);
|
|
|
|
last->next = allObjs;
|
|
|
|
allObjs = all;
|
2003-12-09 02:01:05 +00:00
|
|
|
}
|
|
|
|
if (frames % 50 == 0 && Garbage_Pending()) {
|
|
|
|
Sys_DPrintf("GC: Disposing...\n");
|
2003-12-09 06:57:35 +00:00
|
|
|
Garbage_Dispose (&allObjs, Garbage_Pending()/2 + 1);
|
2003-12-09 02:01:05 +00:00
|
|
|
}
|
2003-12-07 22:45:32 +00:00
|
|
|
}
|