2 Copyright © 1995-2013, 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 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
);
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
;
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
) )
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
))
605 current_if
= inst
->rootif
;
606 *(msg
->num_methods_ptr
) = num_Root_Methods
;
607 *(msg
->interface_id_ptr
) = IID_Root
;
611 current_if
= inst
->metaif
;
612 *(msg
->num_methods_ptr
) = NUMTOTAL_M_Meta
;
613 *(msg
->interface_id_ptr
) = IID_Meta
;
621 (*(msg
->iterval_ptr
)) ++;
626 /* Should never get here, unless someone has created an instance
627 of the BaseMeta class (which is meaningless)
634 D(bug("Current IF: %s, num_methods %ld\n",
635 *(msg
->interface_id_ptr
), *(msg
->num_methods_ptr
)));
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
;
657 EnterFunc(bug("basemeta_dosupermethod(cl=%p, o=%p, msg=%p)\n",
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
)
687 ifm
= &(OOPBase
->ob_BaseMetaObject
.inst
.rootif
[method_offset
]);
691 ifm
= &(OOPBase
->ob_BaseMetaObject
.inst
.metaif
[method_offset
]);
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 */
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
;
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
);