2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
5 Desc: OOP base metaclass
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>
19 #include "basemetaclass.h"
25 #include <aros/debug.h>
27 #define MD(x) ((struct metadata *)x)
29 /*****************************************************************************************
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,
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.
68 Num_Timer_Methods // number of methods in the Timer interface
71 // Attribute offsets for attrs in the IID_Timer interface.
76 Num_Timer_Attrs // number of attrs in the timer interface
79 // private instance data
82 struct timeval start_time;
83 struct timeval elapsed_time;
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);
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));
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)},
135 // Make it a class of the SIMeta
136 tcl = (Class *)OOP_NewObject(NULL, CLID_SIMeta, tags);
140 // Make the class public
147 VOID free_timerclass(Class *cl)
149 OOP_DisposeObject((Object *)cl);
154 *****************************************************************************************/
156 /*****************************************************************************************
159 --naming_conventions--
165 This section describes the recommented convention for naming attributes and methods.
167 Method and attribute offsets are constructed like this:
170 mo<interface>_<method name> (eg. moTimer_Start)
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 /*****************************************************************************************
199 Specifies the class ID for the class.
201 *****************************************************************************************/
203 /*****************************************************************************************
215 ID of public class that will be superclass of class to be created.
217 *****************************************************************************************/
219 /*****************************************************************************************
231 Pointer to private class that will be superclass to
234 *****************************************************************************************/
236 /*****************************************************************************************
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 /*****************************************************************************************
254 aoMeta_InterfaceDescr
257 [I..], struct InterfaceDescr *
263 Pointer to an array of interface descriptors (struct InterfaceDescr).
264 This array has to be null-terminated.
268 struct InterfaceDescr
270 struct MethodDescr *MethodTable;
271 CONST_STRPTR InterfaceID;
275 describes an interface of the class.
276 The MethodTable is an array of
280 IPTR (*MethodFunc)();
284 which describes each method's implementation.
287 struct MethodDescr root_mdescr[NUM_ROOT_METHODS + 1] =
289 { (IPTR (*)())unixio_new, moRoot_New },
290 { (IPTR (*)())unixio_dispose, moRoot_Dispose },
294 struct MethodDescr unixio_mdescr[NUM_UNIXIO_METHODS + 1] =
296 { (IPTR (*)())unixio_wait, moHidd_UnixIO_Wait },
300 struct InterfaceDescr ifdescr[] =
302 {root_mdescr, IID_Root, NUM_ROOT_METHODS},
303 {unixio_mdescr, IID_UnixIO, NUM_UNIXIO_METHODS},
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) },
318 cl = NewObjectA(NULL, CLID_HIDDMeta, tags);
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
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;
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",
378 /* ID of superclass */
379 superid
= (CONST_STRPTR
)tag
->ti_Data
;
380 D(bug("Got superID: %s\n", superid
));
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
;
390 /* The new class' ID */
391 clid
= (CONST_STRPTR
)tag
->ti_Data
;
392 D(bug("Got classID: %s\n", clid
));
395 case aoMeta_SuperPtr
:
396 D(bug("Got superPtr\n"));
397 /* If the super class is private, than we must have
400 superptr
= (struct metadata
*)tag
->ti_Data
;
403 case aoMeta_InstSize
:
404 /* Instance data size for the new class */
405 instsize
= (ULONG
)tag
->ti_Data
;
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 */
420 ReturnPtr ("Meta::New, no ifdescr", OOP_Object
*, NULL
);
422 /* The new class must have a superclass */
427 superptr
= (struct metadata
*)FindName((struct List
*)&(GetOBase(OOPBase
)->ob_ClassList
), superid
);
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
);
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"));
471 data
->public.InstOffset
= superptr
->public.InstOffset
+ superptr
->instsize
;
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"));
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
);
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
);
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
;
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
) )
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
))
606 current_if
= inst
->rootif
;
607 *(msg
->num_methods_ptr
) = num_Root_Methods
;
608 *(msg
->interface_id_ptr
) = IID_Root
;
612 current_if
= inst
->metaif
;
613 *(msg
->num_methods_ptr
) = NUMTOTAL_M_Meta
;
614 *(msg
->interface_id_ptr
) = IID_Meta
;
622 (*(msg
->iterval_ptr
)) ++;
627 /* Should never get here, unless someone has created an instance
628 of the BaseMeta class (which is meaningless)
635 D(bug("Current IF: %s, num_methods %ld\n",
636 *(msg
->interface_id_ptr
), *(msg
->num_methods_ptr
)));
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
;
658 EnterFunc(bug("basemeta_dosupermethod(cl=%p, o=%p, msg=%p)\n",
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
)
688 ifm
= &(OOPBase
->ob_BaseMetaObject
.inst
.rootif
[method_offset
]);
692 ifm
= &(OOPBase
->ob_BaseMetaObject
.inst
.metaif
[method_offset
]);
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 */
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
;
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
);