Minor fixes to comments.
[AROS.git] / rom / oop / basemetaclass.c
blob33333fe9a13262b79b2ac82573ca7f3704526bd5
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: OOP base metaclass
6 Lang: english
7 */
9 #include <proto/exec.h>
10 #include <proto/oop.h>
11 #include <proto/utility.h>
12 #include <exec/alerts.h>
13 #include <exec/memory.h>
14 #include <oop/oop.h>
15 #include <string.h>
17 #include "intern.h"
18 #include "private.h"
19 #include "basemetaclass.h"
21 #undef SDEBUG
22 #undef DEBUG
23 #define SDEBUG 0
24 #define DEBUG 0
25 #include <aros/debug.h>
27 #define MD(x) ((struct metadata *)x)
29 /*****************************************************************************************
31 NAME
32 --background_meta--
34 LOCATION
35 Meta
37 NOTES
38 Classes are objects of metaclasses, so therefore
39 classes are created with OOP_NewObject().
41 As of now there are three different metaclasses available:
43 mimetaclass (CLID_MIMeta)
44 - Creates classes that supports multiple interfaces.
46 simetaclass (CLID_SIMeta)
47 - Creates classes that can only support single interfaces. Advantage is faster
48 method invocation (doesn't require hashing).
50 How to create a class is best shown through an example.
51 Here is a Timer class with two simple methods,
52 Start and Stop.
53 Note, this example doesn't show the New and Dispose
54 methods and OOP_DoSuperMethod() usage, but it is exactly the same as in BOOPSI,
55 so those familiar with BOOPSI, should find creating classes with this system simple.
57 // In the classes' include file you have to define class ID, interface ID
58 // method offsets and attribute offset
59 #define CLID_Timer "timerclass"
60 #define IID_Timer "I_timer"
62 // Method offset for methods in the IID_Timer interface.
63 enum
65 moTimer_Start = 0,
66 moTimer_Stop,
68 Num_Timer_Methods // number of methods in the Timer interface
71 // Attribute offsets for attrs in the IID_Timer interface.
72 enum
74 aoTimer_Elapsed = 0,
76 Num_Timer_Attrs // number of attrs in the timer interface
79 // private instance data
80 struct timer_data
82 struct timeval start_time;
83 struct timeval elapsed_time;
86 // The methods
87 static VOID timer_start(Class *cl, Object *o, Msg msg)
89 struct timer_data *data;
91 data = INST_DATA(tcl, o);
93 gettimeofday(&(data->start_time), NULL);
95 return;
98 static VOID timer_stop(Class *cl, Object *o, Msg msg)
100 struct timer_data *data = INST_DATA(tcl, o);
101 gettimeofday(&(data->elapsed_time), NULL);
103 SubTime(&(data->elapsed_time), &(data->start_time));
105 return;
108 #define NUM_TIMER_METHODS 2
109 Class *make_timerclass()
111 struct MethodDescr methods[NUM_TIMER_METHODS + 1] =
113 {(IPTR (*)())timer_start, moTimer_Start},
114 {(IPTR (*)())timer_stop, moTimer_Stop},
115 {NULL, 0UL} // must be null-terminated
118 struct InterfaceDescr ifdescr[] =
120 { methods, "Timer", NUM_TIMER_METHODS },
121 { NULL, 0UL, 0UL} // must be null-terminated
124 struct TagItem tags[] =
126 {aMeta_SuperID, (IPTR)CLID_Root},
127 {aMeta_InterfaceDescr, (IPTR)ifdescr},
128 {aMeta_ID, (IPTR)CLID_Timer},
129 {aMeta_InstSize, (IPTR)sizeof (struct timer_data)},
130 {TAG_DONE, 0UL}
133 Class *tcl;
135 // Make it a class of the SIMeta
136 tcl = (Class *)OOP_NewObject(NULL, CLID_SIMeta, tags);
138 if (tcl)
140 // Make the class public
141 OOP_AddClass(tcl);
144 return tcl;
147 VOID free_timerclass(Class *cl)
149 OOP_DisposeObject((Object *)cl);
151 return;
154 *****************************************************************************************/
156 /*****************************************************************************************
158 NAME
159 --naming_conventions--
161 LOCATION
162 Meta
164 NOTES
165 This section describes the recommented convention for naming attributes and methods.
167 Method and attribute offsets are constructed like this:
169 method offset:
170 mo<interface>_<method name> (eg. moTimer_Start)
172 attribute offset:
173 ao<interface>_<attrname> (eg. aoTimer_Elapsed)
175 or moHidd_GC_SetPixel and aoHidd_GC_FgPen
177 Macro specifying class ID is defined like this:
178 CLID_<system>_<class name> (eg. CLID_Hidd_Gfx )
180 And interface IDs like this.
181 IID_<system>_<interface name> (eg. IID_Hidd_Gfx )
183 ID themselves are strings.
185 *****************************************************************************************/
187 /*****************************************************************************************
189 NAME
190 aoMeta_ID
192 SYNOPSIS
193 [I..], CONST_STRPTR
195 LOCATION
196 Meta
198 FUNCTION
199 Specifies the class ID for the class.
201 *****************************************************************************************/
203 /*****************************************************************************************
205 NAME
206 aoMeta_SuperID
208 SYNOPSIS
209 [I..], CONST_STRPTR
211 LOCATION
212 Meta
214 FUNCTION
215 ID of public class that will be superclass of class to be created.
217 *****************************************************************************************/
219 /*****************************************************************************************
221 NAME
222 aoMeta_SuperPtr
224 SYNOPSIS
225 [I..], OOP_Class *
227 LOCATION
228 Meta
230 FUNCTION
231 Pointer to private class that will be superclass to
232 class created.
234 *****************************************************************************************/
236 /*****************************************************************************************
238 NAME
239 aoMeta_InstSize
241 SYNOPSIS
242 [I..], ULONG
244 FUNCTION
245 Size of the instance data for this class.
246 Note, this is not necessarily the same as the size of the whole
247 object of this class.
249 *****************************************************************************************/
251 /*****************************************************************************************
253 NAME
254 aoMeta_InterfaceDescr
256 SYNOPSIS
257 [I..], struct InterfaceDescr *
259 LOCATION
260 Meta
262 FUNCTION
263 Pointer to an array of interface descriptors (struct InterfaceDescr).
264 This array has to be null-terminated.
266 Each
268 struct InterfaceDescr
270 struct MethodDescr *MethodTable;
271 CONST_STRPTR InterfaceID;
272 ULONG NumMethods;
275 describes an interface of the class.
276 The MethodTable is an array of
278 struct MethodDescr
280 IPTR (*MethodFunc)();
281 ULONG MethodIdx;
284 which describes each method's implementation.
286 EXAMPLE
287 struct MethodDescr root_mdescr[NUM_ROOT_METHODS + 1] =
289 { (IPTR (*)())unixio_new, moRoot_New },
290 { (IPTR (*)())unixio_dispose, moRoot_Dispose },
291 { NULL, 0UL }
294 struct MethodDescr unixio_mdescr[NUM_UNIXIO_METHODS + 1] =
296 { (IPTR (*)())unixio_wait, moHidd_UnixIO_Wait },
297 { NULL, 0UL }
300 struct InterfaceDescr ifdescr[] =
302 {root_mdescr, IID_Root, NUM_ROOT_METHODS},
303 {unixio_mdescr, IID_UnixIO, NUM_UNIXIO_METHODS},
304 {NULL, NULL, 0UL}
307 struct TagItem tags[] =
309 {aMeta_SuperID, (IPTR)CLID_Hidd},
310 {aMeta_InterfaceDescr, (IPTR)ifdescr},
311 {aMeta_ID, (IPTR)CLID_UnixIO_Hidd},
312 {aMeta_InstSize, (IPTR)sizeof (struct UnixIOData) },
313 {TAG_DONE, 0UL}
318 cl = NewObjectA(NULL, CLID_HIDDMeta, tags);
320 BUGS
321 InterfaceDescr->NumMethods field was originally intended to specify
322 size of internal method table. When creating a new interface (i. e.
323 if this is your own interface), you need to be sure that the value
324 you set there is equal to highest possible method number + 1.
326 Since v42.1 oop.library always ensures that methods table has enough
327 entries to accomodate all defined methods. NumMethods field in interface
328 descriptor is effectively ignored and is present only for backwards
329 compatibility.
331 *****************************************************************************************/
333 /***************************
334 ** RootClass' metaclass **
335 ***************************/
336 /* The root class' meta class is not really needed, but
337 it makes code of other metaclasses more consistent
340 #define IS_META_ATTR(attr, idx) ( (idx = attr - MetaAttrBase) < num_Meta_Attrs )
341 /**********************
342 ** BaseMeta::New() **
343 **********************/
344 static OOP_Object *basemeta_new(OOP_Class *cl, OOP_Object *o, struct pRoot_New *msg)
346 struct IntOOPBase *OOPBase = (struct IntOOPBase *)cl->OOPBasePtr;
347 struct Library *UtilityBase = OOPBase->ob_UtilityBase;
348 struct metadata *data;
350 struct OOP_InterfaceDescr *ifdescr = NULL;
351 CONST_STRPTR superid = NULL, clid = "-- private class -- ";
352 struct metadata *superptr = NULL;
353 struct TagItem *tag, *tstate;
354 ULONG instsize = (ULONG)-1L;
356 ULONG idx;
358 EnterFunc(bug("BaseMeta::New(cl=%s, msg = %p)\n",
359 cl->ClassNode.ln_Name, msg));
361 /* Analyze the taglist before object is allocated,
362 ** so we can easily exit cleanly if some info is missing
365 tstate = msg->attrList;
367 while ((tag = NextTagItem(&tstate)))
369 if (IS_META_ATTR(tag->ti_Tag, idx))
371 D(bug("Got meta attr %lx with TagIdx %ld\n",
372 tag->ti_Tag, idx ));
374 switch (idx)
377 case aoMeta_SuperID:
378 /* ID of superclass */
379 superid = (CONST_STRPTR)tag->ti_Data;
380 D(bug("Got superID: %s\n", superid));
381 break;
383 case aoMeta_InterfaceDescr:
384 D(bug("Got ifdescr\n"));
385 /* What interfaces does the class support ? */
386 ifdescr = (struct OOP_InterfaceDescr *)tag->ti_Data;
387 break;
389 case aoMeta_ID:
390 /* The new class' ID */
391 clid = (CONST_STRPTR)tag->ti_Data;
392 D(bug("Got classID: %s\n", clid));
393 break;
395 case aoMeta_SuperPtr:
396 D(bug("Got superPtr\n"));
397 /* If the super class is private, than we must have
398 a pointer to it.
400 superptr = (struct metadata *)tag->ti_Data;
401 break;
403 case aoMeta_InstSize:
404 /* Instance data size for the new class */
405 instsize = (ULONG)tag->ti_Data;
406 break;
414 /* The user must supply instance size */
415 if (instsize == (ULONG)-1)
416 ReturnPtr ("Meta::New, no instsize", OOP_Object *, NULL);
418 /* The new class must have interfaces */
419 if (!ifdescr)
420 ReturnPtr ("Meta::New, no ifdescr", OOP_Object *, NULL);
422 /* The new class must have a superclass */
423 if (!superptr)
425 if (superid)
427 superptr = (struct metadata *)FindName((struct List *)&(GetOBase(OOPBase)->ob_ClassList), superid);
428 if (!superptr)
429 ReturnPtr ("Meta::New, no superptr/id", OOP_Object *, NULL);
433 /* We are sure we have enough args, and can let rootclass alloc the instance data */
434 o = (OOP_Object *)OOP_DoSuperMethod((OOP_Class *)cl, o, (OOP_Msg)msg);
435 if (o)
438 ULONG dispose_mid = OOP_GetMethodID(IID_Root, moRoot_Dispose);
440 D(bug("Instance allocated\n"));
442 data = OOP_INST_DATA(cl, o);
444 D(bug("o=%p,data=%p\n", o, data));
445 D(bug("instoffset: %ld\n", cl->InstOffset));
447 /* Clear instdata, so we in Dispose() can see what's been allocated */
448 memset(data, 0, sizeof (struct metadata));
450 D(bug("superptr=%p\n", superptr));
452 data->public.OOPBasePtr = (struct IntOOPBase *)OOPBase;
455 /* Let subclass create an initialize dispatch tables for the new class object*/
456 if (meta_allocdisptabs(o, (OOP_Class *)superptr, ifdescr))
458 data->disptabs_inited = TRUE;
460 /* Copy the class' ID */
461 D(bug("Allocating class ID\n"));
462 data->public.ClassNode.ln_Name = AllocVec(strlen (clid) + 1, MEMF_ANY);
463 if (data->public.ClassNode.ln_Name)
465 D(bug("class ID allocated\n"));
467 /* Initialize class fields */
468 D(bug("Setting instoffset\n"));
469 /* Instoffset */
470 if (superptr)
471 data->public.InstOffset = superptr->public.InstOffset + superptr->instsize;
472 else
473 data->public.InstOffset = 0UL;
474 D(bug("Setting other stuff\n"));
476 data->subclasscount = 0UL;
477 data->objectcount = 0UL;
478 data->public.superclass = (OOP_Class *)superptr;
479 data->instsize = instsize;
481 D(bug("Copying class ID\n"));
482 /* Copy class ID */
483 strcpy(data->public.ClassNode.ln_Name, clid);
486 ReturnPtr ("Meta::New", OOP_Object *, o);
490 D(bug("Failed to allocate dispatch tables\n"));
492 OOP_CoerceMethod((OOP_Class *)cl, o, (OOP_Msg)&dispose_mid);
495 ReturnPtr ("Meta::New", OOP_Object *, NULL);
499 /**************************
500 ** BaseMeta::Dispose() **
501 **************************/
502 static VOID basemeta_dispose(OOP_Class *cl, OOP_Object *o, OOP_Msg msg)
504 struct IntOOPBase *OOPBase = (struct IntOOPBase *)cl->OOPBasePtr;
505 struct metadata *data = OOP_INST_DATA(cl, o);
506 IPTR iterval = 0UL;
507 CONST_STRPTR interface_id = NULL;
508 ULONG num_methods = 0UL;
510 if (data->public.ClassNode.ln_Name)
511 FreeVec(data->public.ClassNode.ln_Name);
513 /* Release interfaces from global interface table */
514 while (meta_iterateifs(o, &iterval, &interface_id, &num_methods))
516 /* Only release the interfaces that were new for the class */
517 if (!meta_getifinfo((OOP_Object *)MD(o)->public.superclass, interface_id, &num_methods))
518 release_idbucket(interface_id, GetOBase(OOPBase));
522 if (data->disptabs_inited)
523 meta_freedisptabs(o);
525 OOP_DoSuperMethod(cl, o, msg);
527 return;
531 /****************************
532 ** BaseMeta::getifinfo() **
533 ****************************/
534 static struct IFMethod *basemeta_getifinfo(OOP_Class *cl, OOP_Object *o, struct P_meta_getifinfo *msg)
536 struct IntOOPBase *OOPBase = (struct IntOOPBase *)cl->OOPBasePtr;
537 struct IFMethod *mtab = NULL;
538 EnterFunc(bug("BaseMeta::hasinterface(cl=%p, o=%p, iid=%s\n",
539 cl, o, msg->interface_id));
541 /* The object passed might be one of two classes: Root class or basemetaclass */
542 if (0 == strcmp(msg->interface_id, IID_Root))
544 /* Both classes support the root interface */
545 D(bug("Root interface\n"));
546 *(msg->num_methods_ptr) = num_Root_Methods;
547 if ( ((OOP_Class *)o) == BASEMETAPTR)
549 mtab = OOPBase->ob_BaseMetaObject.inst.rootif;
551 else
553 mtab = OOPBase->ob_RootClassObject.inst.rootif;
557 else if (0 == strcmp(msg->interface_id, IID_Meta))
559 D(bug("Meta interface. BASEMETAPTR: %p\n", BASEMETAPTR));
560 if ( ((OOP_Class *)o) == BASEMETAPTR )
563 /* Only BaseMeta has Meta interface */
564 mtab = OOPBase->ob_BaseMetaObject.inst.metaif;
565 *(msg->num_methods_ptr) = NUMTOTAL_M_Meta;
569 ReturnPtr ("BaseMeta::hasinterface", struct IFMethod *, mtab);
573 /*****************************
574 ** BaseMeta::iterateifs() **
575 *****************************/
576 static struct IFMethod *basemeta_iterateifs(
577 OOP_Class *cl, OOP_Object *o, struct P_meta_iterateifs *msg)
579 struct IntOOPBase *OOPBase = (struct IntOOPBase *)cl->OOPBasePtr;
580 struct IFMethod *current_if = NULL;
582 EnterFunc(bug("BaseMeta::iterateifs(o=%p)\n", o));
584 /* As in has_interface() the object here can only be the basemetaclass, or rootclass */
585 if (((OOP_Class *)o) == ROOTCLASSPTR)
587 /* Rootclass have only one interface */
588 if ( *(msg->iterval_ptr) )
590 current_if = NULL;
592 else
594 current_if = OOPBase->ob_RootClassObject.inst.rootif;
595 *(msg->num_methods_ptr) = num_Root_Methods;
596 *(msg->interface_id_ptr) = IID_Root;
597 *(msg->iterval_ptr) = 1UL; /* We're through iterating */
600 else if (((OOP_Class *)o) == BASEMETAPTR)
602 struct basemeta_inst *inst = (struct basemeta_inst *)o;
603 switch (*(msg->iterval_ptr))
605 case 0:
606 current_if = inst->rootif;
607 *(msg->num_methods_ptr) = num_Root_Methods;
608 *(msg->interface_id_ptr) = IID_Root;
609 break;
611 case 1:
612 current_if = inst->metaif;
613 *(msg->num_methods_ptr) = NUMTOTAL_M_Meta;
614 *(msg->interface_id_ptr) = IID_Meta;
615 break;
617 default:
618 current_if = NULL;
619 break;
622 (*(msg->iterval_ptr)) ++;
625 else
627 /* Should never get here, unless someone has created an instance
628 of the BaseMeta class (which is meaningless)
630 current_if = NULL;
632 #if DEBUG
633 if (current_if)
635 D(bug("Current IF: %s, num_methods %ld\n",
636 *(msg->interface_id_ptr), *(msg->num_methods_ptr)));
638 #endif
639 ReturnPtr ("BaseMeta::iterate_ifs", struct IFMethod *, current_if);
643 /*******************************
644 ** BaseMeta DoSuperMethod() **
645 *******************************/
646 /* cl->USerData passed to DoSuperMethodA might be
647 a subclass of rootclass, which does not have
648 the OOPBase in cl->UserData, so instead we use the
649 meta's UserData (IFMeta or HIDDMeta class
652 IPTR basemeta_dosupermethod(OOP_Class *cl, OOP_Object *o, OOP_Msg msg)
654 struct IntOOPBase *OOPBase = (struct IntOOPBase *)cl->OOPBasePtr;
655 struct IFMethod *ifm;
656 IPTR ret;
658 EnterFunc(bug("basemeta_dosupermethod(cl=%p, o=%p, msg=%p)\n",
659 cl, o, msg));
661 if (MD(cl)->public.superclass == ROOTCLASSPTR)
663 ifm = &(OOPBase->ob_RootClassObject.inst.rootif[*msg]);
665 ret = ifm->MethodFunc(ifm->mClass, o, msg);
667 else /* superclass is the BaseMeta class */
668 ret = basemeta_coercemethod(cl, o, msg);
670 ReturnPtr ("basemeta_dosupermethod", IPTR, ret);
673 /*******************************
674 ** BaseMeta CoerceMethod() **
675 *******************************/
676 IPTR basemeta_coercemethod(OOP_Class *cl, OOP_Object *o, OOP_Msg msg)
678 struct IntOOPBase *OOPBase = (struct IntOOPBase *)cl->OOPBasePtr;
679 ULONG method_offset = *msg & METHOD_MASK;
680 struct IFMethod *ifm;
682 EnterFunc(bug("basemeta_coercemethod(cl=%p, o=%p, msg=%p)\n", cl, o, msg));
684 switch (*msg >> NUM_METHOD_BITS)
687 case 0:
688 ifm = &(OOPBase->ob_BaseMetaObject.inst.rootif[method_offset]);
689 break;
691 case 1:
692 ifm = &(OOPBase->ob_BaseMetaObject.inst.metaif[method_offset]);
693 break;
696 default:
697 bug("Error: basemeta_coercemethod got method call to unknown interface %d\n", *msg >> NUM_METHOD_BITS);
699 /* Throw an alert to be able to see a stacktrace */
700 Alert(AN_OOP);
701 return 0;
703 ReturnPtr ("basemeta_coercemethod", IPTR, ifm->MethodFunc(ifm->mClass, o, msg));
706 /************************
707 ** BaseMeta DoMethod **
708 ************************/
710 IPTR basemeta_domethod(OOP_Object *o, OOP_Msg msg)
712 return basemeta_coercemethod(OOP_OCLASS(o), o, msg);
715 /**********************
716 ** init_basemeta() **
717 **********************/
719 BOOL init_basemeta(struct IntOOPBase *OOPBase)
721 struct basemetaobject *bmo;
722 BOOL success;
723 ULONG mbase = 0;
725 EnterFunc(bug("init_basemeta()\n"));
727 bmo = &(OOPBase->ob_BaseMetaObject);
728 bmo->oclass = BASEMETAPTR;
730 bmo->inst.data.public.ClassNode.ln_Name = "private base metaclass";
731 bmo->inst.data.public.OOPBasePtr = OOPBase;
732 bmo->inst.data.public.InstOffset = 0UL;
733 bmo->inst.data.public.UserData = OOPBase;
734 bmo->inst.data.public.cl_DoSuperMethod = basemeta_dosupermethod;
735 bmo->inst.data.public.cl_CoerceMethod = basemeta_coercemethod;
736 bmo->inst.data.public.cl_DoMethod = basemeta_domethod;
738 bmo->inst.data.public.superclass = ROOTCLASSPTR;
739 bmo->inst.data.subclasscount = 0UL;
740 bmo->inst.data.objectcount = 0UL;
741 bmo->inst.data.instsize = sizeof (struct metadata);
742 bmo->inst.data.numinterfaces = NUM_BASEMETA_IFS;
744 /* Initialize interface table */
745 bmo->inst.iftable[0] = bmo->inst.rootif;
746 bmo->inst.iftable[1] = bmo->inst.metaif;
748 /* initialize interfaces */
749 bmo->inst.rootif[moRoot_New].MethodFunc = (IPTR (*)())basemeta_new;
750 bmo->inst.rootif[moRoot_Dispose].MethodFunc = (IPTR (*)())basemeta_dispose;
752 bmo->inst.rootif[moRoot_New].mClass = BASEMETAPTR;
753 bmo->inst.rootif[moRoot_Dispose].mClass = BASEMETAPTR;
755 /* Initialize meta interface */
756 bmo->inst.metaif[MO_meta_allocdisptabs].MethodFunc = (IPTR (*)())NULL;
757 bmo->inst.metaif[MO_meta_freedisptabs].MethodFunc = (IPTR (*)())NULL;
758 bmo->inst.metaif[MO_meta_getifinfo].MethodFunc = (IPTR (*)())basemeta_getifinfo;
759 bmo->inst.metaif[MO_meta_iterateifs].MethodFunc = (IPTR (*)())basemeta_iterateifs;
761 bmo->inst.metaif[MO_meta_allocdisptabs].mClass = BASEMETAPTR;
762 bmo->inst.metaif[MO_meta_freedisptabs].mClass = BASEMETAPTR;
763 bmo->inst.metaif[MO_meta_getifinfo].mClass = BASEMETAPTR;
764 bmo->inst.metaif[MO_meta_iterateifs].mClass = BASEMETAPTR;
766 /* Meta interface ID gets initialized to 1 */
767 success = init_mi_methodbase(IID_Meta, &mbase, OOPBase);
769 ReturnBool ("init_basemeta", success);