Added useful custom dimensions to some top-level drawers that lacked them.
[AROS.git] / rom / oop / basemetaclass.c
blobd287ea893ae8f88edd5b02ec1982b6e07cca8aee
1 /*
2 Copyright © 1995-2013, 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 FreeVec(data->public.ClassNode.ln_Name);
512 /* Release interfaces from global interface table */
513 while (meta_iterateifs(o, &iterval, &interface_id, &num_methods))
515 /* Only release the interfaces that were new for the class */
516 if (!meta_getifinfo((OOP_Object *)MD(o)->public.superclass, interface_id, &num_methods))
517 release_idbucket(interface_id, GetOBase(OOPBase));
521 if (data->disptabs_inited)
522 meta_freedisptabs(o);
524 OOP_DoSuperMethod(cl, o, msg);
526 return;
530 /****************************
531 ** BaseMeta::getifinfo() **
532 ****************************/
533 static struct IFMethod *basemeta_getifinfo(OOP_Class *cl, OOP_Object *o, struct P_meta_getifinfo *msg)
535 struct IntOOPBase *OOPBase = (struct IntOOPBase *)cl->OOPBasePtr;
536 struct IFMethod *mtab = NULL;
537 EnterFunc(bug("BaseMeta::hasinterface(cl=%p, o=%p, iid=%s\n",
538 cl, o, msg->interface_id));
540 /* The object passed might be one of two classes: Root class or basemetaclass */
541 if (0 == strcmp(msg->interface_id, IID_Root))
543 /* Both classes support the root interface */
544 D(bug("Root interface\n"));
545 *(msg->num_methods_ptr) = num_Root_Methods;
546 if ( ((OOP_Class *)o) == BASEMETAPTR)
548 mtab = OOPBase->ob_BaseMetaObject.inst.rootif;
550 else
552 mtab = OOPBase->ob_RootClassObject.inst.rootif;
556 else if (0 == strcmp(msg->interface_id, IID_Meta))
558 D(bug("Meta interface. BASEMETAPTR: %p\n", BASEMETAPTR));
559 if ( ((OOP_Class *)o) == BASEMETAPTR )
562 /* Only BaseMeta has Meta interface */
563 mtab = OOPBase->ob_BaseMetaObject.inst.metaif;
564 *(msg->num_methods_ptr) = NUMTOTAL_M_Meta;
568 ReturnPtr ("BaseMeta::hasinterface", struct IFMethod *, mtab);
572 /*****************************
573 ** BaseMeta::iterateifs() **
574 *****************************/
575 static struct IFMethod *basemeta_iterateifs(
576 OOP_Class *cl, OOP_Object *o, struct P_meta_iterateifs *msg)
578 struct IntOOPBase *OOPBase = (struct IntOOPBase *)cl->OOPBasePtr;
579 struct IFMethod *current_if = NULL;
581 EnterFunc(bug("BaseMeta::iterateifs(o=%p)\n", o));
583 /* As in has_interface() the object here can only be the basemetaclass, or rootclass */
584 if (((OOP_Class *)o) == ROOTCLASSPTR)
586 /* Rootclass have only one interface */
587 if ( *(msg->iterval_ptr) )
589 current_if = NULL;
591 else
593 current_if = OOPBase->ob_RootClassObject.inst.rootif;
594 *(msg->num_methods_ptr) = num_Root_Methods;
595 *(msg->interface_id_ptr) = IID_Root;
596 *(msg->iterval_ptr) = 1UL; /* We're through iterating */
599 else if (((OOP_Class *)o) == BASEMETAPTR)
601 struct basemeta_inst *inst = (struct basemeta_inst *)o;
602 switch (*(msg->iterval_ptr))
604 case 0:
605 current_if = inst->rootif;
606 *(msg->num_methods_ptr) = num_Root_Methods;
607 *(msg->interface_id_ptr) = IID_Root;
608 break;
610 case 1:
611 current_if = inst->metaif;
612 *(msg->num_methods_ptr) = NUMTOTAL_M_Meta;
613 *(msg->interface_id_ptr) = IID_Meta;
614 break;
616 default:
617 current_if = NULL;
618 break;
621 (*(msg->iterval_ptr)) ++;
624 else
626 /* Should never get here, unless someone has created an instance
627 of the BaseMeta class (which is meaningless)
629 current_if = NULL;
631 #if DEBUG
632 if (current_if)
634 D(bug("Current IF: %s, num_methods %ld\n",
635 *(msg->interface_id_ptr), *(msg->num_methods_ptr)));
637 #endif
638 ReturnPtr ("BaseMeta::iterate_ifs", struct IFMethod *, current_if);
642 /*******************************
643 ** BaseMeta DoSuperMethod() **
644 *******************************/
645 /* cl->USerData passed to DoSuperMethodA might be
646 a subclass of rootclass, which does not have
647 the OOPBase in cl->UserData, so instead we use the
648 meta's UserData (IFMeta or HIDDMeta class
651 IPTR basemeta_dosupermethod(OOP_Class *cl, OOP_Object *o, OOP_Msg msg)
653 struct IntOOPBase *OOPBase = (struct IntOOPBase *)cl->OOPBasePtr;
654 struct IFMethod *ifm;
655 IPTR ret;
657 EnterFunc(bug("basemeta_dosupermethod(cl=%p, o=%p, msg=%p)\n",
658 cl, o, msg));
660 if (MD(cl)->public.superclass == ROOTCLASSPTR)
662 ifm = &(OOPBase->ob_RootClassObject.inst.rootif[*msg]);
664 ret = ifm->MethodFunc(ifm->mClass, o, msg);
666 else /* superclass is the BaseMeta class */
667 ret = basemeta_coercemethod(cl, o, msg);
669 ReturnPtr ("basemeta_dosupermethod", IPTR, ret);
672 /*******************************
673 ** BaseMeta CoerceMethod() **
674 *******************************/
675 IPTR basemeta_coercemethod(OOP_Class *cl, OOP_Object *o, OOP_Msg msg)
677 struct IntOOPBase *OOPBase = (struct IntOOPBase *)cl->OOPBasePtr;
678 ULONG method_offset = *msg & METHOD_MASK;
679 struct IFMethod *ifm;
681 EnterFunc(bug("basemeta_coercemethod(cl=%p, o=%p, msg=%p)\n", cl, o, msg));
683 switch (*msg >> NUM_METHOD_BITS)
686 case 0:
687 ifm = &(OOPBase->ob_BaseMetaObject.inst.rootif[method_offset]);
688 break;
690 case 1:
691 ifm = &(OOPBase->ob_BaseMetaObject.inst.metaif[method_offset]);
692 break;
695 default:
696 bug("Error: basemeta_coercemethod got method call to unknown interface %d\n", *msg >> NUM_METHOD_BITS);
698 /* Throw an alert to be able to see a stacktrace */
699 Alert(AN_OOP);
700 return 0;
702 ReturnPtr ("basemeta_coercemethod", IPTR, ifm->MethodFunc(ifm->mClass, o, msg));
705 /************************
706 ** BaseMeta DoMethod **
707 ************************/
709 IPTR basemeta_domethod(OOP_Object *o, OOP_Msg msg)
711 return basemeta_coercemethod(OOP_OCLASS(o), o, msg);
714 /**********************
715 ** init_basemeta() **
716 **********************/
718 BOOL init_basemeta(struct IntOOPBase *OOPBase)
720 struct basemetaobject *bmo;
721 BOOL success;
722 ULONG mbase = 0;
724 EnterFunc(bug("init_basemeta()\n"));
726 bmo = &(OOPBase->ob_BaseMetaObject);
727 bmo->oclass = BASEMETAPTR;
729 bmo->inst.data.public.ClassNode.ln_Name = "private base metaclass";
730 bmo->inst.data.public.OOPBasePtr = OOPBase;
731 bmo->inst.data.public.InstOffset = 0UL;
732 bmo->inst.data.public.UserData = OOPBase;
733 bmo->inst.data.public.cl_DoSuperMethod = basemeta_dosupermethod;
734 bmo->inst.data.public.cl_CoerceMethod = basemeta_coercemethod;
735 bmo->inst.data.public.cl_DoMethod = basemeta_domethod;
737 bmo->inst.data.public.superclass = ROOTCLASSPTR;
738 bmo->inst.data.subclasscount = 0UL;
739 bmo->inst.data.objectcount = 0UL;
740 bmo->inst.data.instsize = sizeof (struct metadata);
741 bmo->inst.data.numinterfaces = NUM_BASEMETA_IFS;
743 /* Initialize interface table */
744 bmo->inst.iftable[0] = bmo->inst.rootif;
745 bmo->inst.iftable[1] = bmo->inst.metaif;
747 /* initialize interfaces */
748 bmo->inst.rootif[moRoot_New].MethodFunc = (IPTR (*)())basemeta_new;
749 bmo->inst.rootif[moRoot_Dispose].MethodFunc = (IPTR (*)())basemeta_dispose;
751 bmo->inst.rootif[moRoot_New].mClass = BASEMETAPTR;
752 bmo->inst.rootif[moRoot_Dispose].mClass = BASEMETAPTR;
754 /* Initialize meta interface */
755 bmo->inst.metaif[MO_meta_allocdisptabs].MethodFunc = (IPTR (*)())NULL;
756 bmo->inst.metaif[MO_meta_freedisptabs].MethodFunc = (IPTR (*)())NULL;
757 bmo->inst.metaif[MO_meta_getifinfo].MethodFunc = (IPTR (*)())basemeta_getifinfo;
758 bmo->inst.metaif[MO_meta_iterateifs].MethodFunc = (IPTR (*)())basemeta_iterateifs;
760 bmo->inst.metaif[MO_meta_allocdisptabs].mClass = BASEMETAPTR;
761 bmo->inst.metaif[MO_meta_freedisptabs].mClass = BASEMETAPTR;
762 bmo->inst.metaif[MO_meta_getifinfo].mClass = BASEMETAPTR;
763 bmo->inst.metaif[MO_meta_iterateifs].mClass = BASEMETAPTR;
765 /* Meta interface ID gets initialized to 1 */
766 success = init_mi_methodbase(IID_Meta, &mbase, OOPBase);
768 ReturnBool ("init_basemeta", success);