2 Copyright © 1995-2001, The AROS Development Team. All rights reserved.
9 #include <proto/exec.h>
10 #include <proto/utility.h>
11 #include <exec/memory.h>
13 #include <proto/oop.h>
24 #include <aros/debug.h>
26 #define MD(cl) ((struct metadata *)cl)
27 #define IFI(cl) ((struct ifmeta_inst *)cl)
29 # define IntCallMethod(cl, o, msg) \
31 register struct IFBucket *b; \
32 register OOP_MethodID mid = *msg; \
33 register ULONG ifid = mid & (~METHOD_MASK); \
34 register struct IFMethod *method; \
38 b = IFI(cl)->data.iftab_directptr[ifid & IFI(cl)->data.hashmask]; \
41 if (b->InterfaceID == ifid) \
43 method = &(b->MethodTable[mid]); \
44 return (method->MethodFunc(method->mClass, o, msg)); \
53 #define UB(x) ((UBYTE *)x)
55 /* Allocates and initializes the interface hashtable, and the methodtables */
56 static BOOL
ifmeta_allocdisptabs(OOP_Class
*cl
, OOP_Object
*o
, struct P_meta_allocdisptabs
*msg
);
57 static VOID
ifmeta_freedisptabs(OOP_Class
*cl
, OOP_Object
*o
, OOP_Msg msg
);
59 static IPTR
Meta_DoMethod(OOP_Object
*o
, OOP_Msg msg
);
60 static IPTR
Meta_CoerceMethod(OOP_Class
*cl
, OOP_Object
*o
, OOP_Msg msg
);
61 static IPTR
Meta_DoSuperMethod(OOP_Class
*cl
, OOP_Object
*o
, OOP_Msg msg
);
64 VOID
freebucket(struct Bucket
*b
, struct IntOOPBase
*OOPBase
);
65 struct Bucket
*copybucket(struct Bucket
*old_b
, APTR data
, struct IntOOPBase
*OOPBase
);
68 static struct IFBucket
*createbucket(
71 ,struct IntOOPBase
*OOPBase
);
73 static ULONG
calc_ht_entries(struct ifmeta_inst
*cl
75 ,struct OOP_InterfaceDescr
*ifDescr
76 ,struct IntOOPBase
*OOPBase
);
81 The metaclass is used to create class. That means,
82 classes are instances of the meta class.
83 The meta class is itself both a class (you can
84 create instances of it), and an object (you can invoke
89 #define OOPBase (GetOBase(cl->OOPBasePtr))
94 static OOP_Object
*ifmeta_new(OOP_Class
*cl
, OOP_Object
*o
, struct pRoot_New
*msg
)
97 IPTR (*domethod
)(OOP_Object
*, OOP_Msg
) = NULL
;
98 IPTR (*coercemethod
)(OOP_Class
*, OOP_Object
*, OOP_Msg
) = NULL
;
99 IPTR (*dosupermethod
)(OOP_Class
*, OOP_Object
*, OOP_Msg
) = NULL
;
101 EnterFunc(bug("IFMeta::New(cl=%s, msg = %p)\n",
102 cl
->ClassNode
.ln_Name
, msg
));
104 /* Let the BaseMeta class initialize some stuff for us */
105 o
= (OOP_Object
*)OOP_DoSuperMethod((OOP_Class
*)cl
, o
, (OOP_Msg
)msg
);
109 struct ifmeta_inst
*inst
;
111 inst
= (struct ifmeta_inst
*)o
;
113 domethod
= (IPTR (*)())GetTagData(aMeta_DoMethod
, NULL
, msg
->attrList
);
114 coercemethod
= (IPTR (*)())GetTagData(aMeta_CoerceMethod
, NULL
, msg
->attrList
);
115 dosupermethod
= (IPTR (*)())GetTagData(aMeta_DoSuperMethod
, NULL
, msg
->attrList
);
118 D(bug("Instance allocated %p\n", inst
));
122 domethod
= Meta_DoMethod
;
125 coercemethod
= Meta_CoerceMethod
;
130 OOP_Class
*superptr
= inst
->base
.superclass
;
133 D(bug("Got superptr: %p\n", superptr
));
134 /* Use superclass' DoSupermethod call if superclass isn't
135 an instance of the HIDDMetaClass
137 if (OOP_OCLASS(superptr
) != (OOP_Class
*)cl
)
139 D(bug("superptr has different meta\n"));
141 dosupermethod
= superptr
->cl_DoSuperMethod
;
145 D(bug("superptr has same meta\n"));
147 dosupermethod
= Meta_DoSuperMethod
;
151 else /* if (class has no superclass) */
153 dosupermethod
= NULL
;
158 inst
->base
.public.OOPBasePtr
= (struct IntOOPBase
*)OOPBase
;
160 inst
->base
.public.cl_DoMethod
= domethod
;
161 inst
->base
.public.cl_CoerceMethod
= coercemethod
;
162 inst
->base
.public.cl_DoSuperMethod
= dosupermethod
;
164 D(bug("Classes' functions set\n"));
167 ReturnPtr ("IFMeta::New", OOP_Object
*, o
);
170 /******************************
171 ** IFMeta::allocdisptabs() **
172 ******************************/
174 /* Allocates and initializes the interface hashtable, and the methodtables */
175 static BOOL
ifmeta_allocdisptabs(OOP_Class
*cl
, OOP_Object
*o
, struct P_meta_allocdisptabs
*msg
)
179 struct ifmeta_inst
*inst
= (struct ifmeta_inst
*)o
;
181 EnterFunc(bug("IFMeta::allocdisptabs(cl=%p, o=%p,ifDescr=%p)\n",
182 cl
, o
, msg
->ifdescr
));
184 /* Get number of needed hash entries */
185 num_if
= calc_ht_entries(inst
, msg
->superclass
, msg
->ifdescr
, OOPBase
);
187 inst
->base
.numinterfaces
= num_if
;
189 D(bug("numinterfaces set to %ld\n", num_if
));
191 /* Create a new integer hashtable, with a reasonable size */
192 inst
->data
.iftable
= NewHash(num_if
, HT_INTEGER
, OOPBase
);
193 if (inst
->data
.iftable
)
195 struct OOP_InterfaceDescr
*ifdescr
;
196 D(bug("Got iftable\n"));
197 /* Save hashmask for use in method lookup */
198 inst
->data
.hashmask
= HashMask(inst
->data
.iftable
);
200 if (msg
->superclass
) /* This test makes it work for initializing root classes */
203 /* Copy parent interfaces into the new class */
204 struct IFMethod
*superif
;
205 struct P_meta_iterateifs ii_msg
;
209 /* must be initialized to zero */
212 D(bug("Adding superclass' methods\n"));
214 ii_msg
.mid
= OOP_GetMethodID(IID_Meta
, MO_meta_iterateifs
);
216 ii_msg
.iterval_ptr
= &iterval
;
217 ii_msg
.interface_id_ptr
= &interface_id
;
218 ii_msg
.num_methods_ptr
= &num_methods
;
223 struct IFBucket
*ifb
;
224 struct IFMethod
*ifm
= NULL
;
227 superif
= (struct IFMethod
*)OOP_CoerceMethod(OOP_OCLASS(msg
->superclass
)
228 ,(OOP_Object
*)msg
->superclass
233 /* Allocate and insert the interface into the new class */
234 ifb
= createbucket(interface_id
, num_methods
, OOPBase
);
235 D(bug("Created bucket: %p\n", ifb
));
239 /* Copy the interface */
240 mtab_size
= UB (&ifm
[num_methods
]) - UB( &ifm
[0]);
241 D(bug("Copying from superclass methods for if %s, mtab_size=%d,basmetaroot %p, superif %p\n",
242 ifb
->GlobalInterfaceID
, mtab_size
, OOPBase
->ob_BaseMetaObject
.inst
.rootif
, superif
));
244 CopyMem(superif
, ifb
->MethodTable
, mtab_size
);
245 InsertBucket(inst
->data
.iftable
, (struct Bucket
*)ifb
, OOPBase
);
249 } /* if (we inherit interfaces from some superclass) */
251 /* Insert our own interfaces */
252 D(bug("Inserting own methods\n"));
253 for ( ifdescr
= msg
->ifdescr
; ifdescr
->MethodTable
; ifdescr
++)
255 struct IFBucket
*ifb
;
259 /* Get variable interface ID */
261 D(bug("Getting Local ifID for global ID %s\n", ifdescr
->InterfaceID
));
262 if (!init_mi_methodbase(ifdescr
->InterfaceID
, &iid
, OOPBase
))
265 D(bug("Got local ifID %ld\n", iid
));
267 /* Lookup hashtable to see if interface has been copied from superclass */
268 ifb
= (struct IFBucket
*)inst
->data
.iftable
->Lookup(
273 D(bug("tried to find bucket in hashtable: %p\n", ifb
));
276 D(bug("Bucket doesn't exist, creating..\n"));
277 /* Bucket doesn't exist, allocate it */
278 ifb
= createbucket(ifdescr
->InterfaceID
, ifdescr
->NumMethods
, OOPBase
);
283 D(bug("Inserting bucket for IF %s\n", ifdescr
->InterfaceID
));
284 InsertBucket(inst
->data
.iftable
, (struct Bucket
*)ifb
, OOPBase
);
289 D(bug("overriding methods\n"));
291 /* Ovveride the superclass methods with our new ones */
292 for (i
= 0; ifdescr
->MethodTable
[i
].MethodFunc
; i
++)
294 if (ifdescr
->MethodTable
[i
].MethodFunc
)
296 ifb
->MethodTable
[ ifdescr
->MethodTable
[i
].MethodIdx
].MethodFunc
= ifdescr
->MethodTable
[i
].MethodFunc
;
297 ifb
->MethodTable
[ ifdescr
->MethodTable
[i
].MethodIdx
].mClass
= (OOP_Class
*)o
;
299 } /* for (each method in the interface) */
301 } /* for (each interface to add to class) */
303 /* For speedup in method lookup */
304 inst
->data
.iftab_directptr
= (struct IFBucket
**)inst
->data
.iftable
->Table
;
306 ReturnBool ("IFMeta::allocdisptabs", TRUE
);
308 } /* if (interface hash table allocated) */
312 if (inst
->data
.iftable
)
313 FreeHash(inst
->data
.iftable
, freebucket
, OOPBase
);
314 ReturnBool ("IFMeta::allocdisptabs", FALSE
);
317 /*****************************
318 ** IFMeta::freedisptabs() **
319 *****************************/
320 static VOID
ifmeta_freedisptabs(OOP_Class
*cl
, OOP_Object
*o
, OOP_Msg msg
)
323 struct ifmeta_inst
*inst
= (struct ifmeta_inst
*)o
;
324 /* This frees the hashtable + all buckets */
326 FreeHash(inst
->data
.iftable
, freebucket
, OOPBase
);
331 /**************************
332 ** IFMeta::getifinfo() **
333 **************************/
334 static struct IFMethod
*ifmeta_getifinfo(OOP_Class
*cl
, OOP_Object
*o
, struct P_meta_getifinfo
*msg
)
337 struct ifmeta_inst
*inst
= (struct ifmeta_inst
*)o
;
338 struct IFMethod
*mtab
= NULL
;
341 /* Get the ULONG variable interface id */
342 iid
= OOP_GetMethodID(msg
->interface_id
, 0);
344 /* Try looking it up in the class' table */
345 b
= (struct IFBucket
*)inst
->data
.iftable
->Lookup(inst
->data
.iftable
, iid
, OOPBase
);
347 *(msg
->num_methods_ptr
) = b
->NumMethods
;
349 mtab
= b
->MethodTable
;
355 /***************************
356 ** IFMeta::iterateifs() **
357 ***************************/
358 static struct IFMethod
*ifmeta_iterateifs(OOP_Class
*cl
, OOP_Object
*o
, struct P_meta_iterateifs
*msg
)
360 struct HashTable
*ht
= ((struct ifmeta_inst
*)o
)->data
.iftable
;
361 struct IFBucket
*found_bucket
= NULL
; /* MUST default to NULL */
363 struct IFBucket
*ifb
= NULL
; /* keep compiler happy */
366 struct IFMethod
*current_if
= NULL
; /* MUST default to NULL */
368 UWORD last_idx
= (*(msg
->iterval_ptr
)) >> 16;
369 UWORD last_bucket_no
= (*(msg
->iterval_ptr
)) & 0x0000FFFF;
373 UWORD bucket_no
= 0; /* keep compiler happy */
374 EnterFunc(bug("IFMeta::iterateifs(cl=%s, o=%s)\n",
375 cl
->ClassNode
.ln_Name
, ((Class
*)o
)->ClassNode
.ln_Name
));
377 D(bug("last_idx: %ld, last_bucket_no=%ld\n", last_idx
, last_bucket_no
));
380 for (idx
= last_idx
;idx
< HashSize(ht
); idx
++)
382 D(bug("idx=%ld\n", idx
));
386 for (ifb
= (struct IFBucket
*)ht
->Table
[idx
]; ifb
; )
388 D(bug("ifb=%s, netx=%p, bucket_no=%ld\n",
389 ifb
->GlobalInterfaceID
, ifb
->Next
, bucket_no
));
390 /* Is this a new bucket in the iteration ? */
392 if ((idx
> last_idx
) || (bucket_no
>= last_bucket_no
))
396 /* Yes, it's a goto, but it really simplifies things here */
405 } /* for (all buckets at each idx) */
407 } /* for (each entry at east index from last idx) */
411 /* OK, found a bucket ? */
414 D(bug("bucket found: %s at idx %ld, b_no %ld\n",
415 found_bucket
->GlobalInterfaceID
, idx
, bucket_no
));
416 *(msg
->iterval_ptr
) = (idx
<< 16) + bucket_no
+ 1;
417 *(msg
->interface_id_ptr
) = ifb
->GlobalInterfaceID
;
418 *(msg
->num_methods_ptr
) = ifb
->NumMethods
;
420 current_if
= ifb
->MethodTable
;
423 ReturnPtr ("IFMeta::iterateifs", struct IFMethod
*, current_if
);
426 /***************************
427 ** IFMeta::findmethod() **
428 ***************************/
430 /* Used for finding a method for method objects */
431 static struct IFMethod
*ifmeta_findmethod(OOP_Class
*cl
, OOP_Object
*o
, struct P_meta_findmethod
*msg
)
433 register struct IFBucket
*b
;
434 register ULONG method_offset
;
435 struct ifmeta_inst
*inst
= (struct ifmeta_inst
*)o
;
437 /* Get interfaceID part of methodID */
438 register ULONG ifid
= msg
->method_to_find
& (~METHOD_MASK
);
440 EnterFunc(bug("IFMeta::findmethod(o=%p, mid=%ld)\n", o
, msg
->method_to_find
));
443 /* Get method offset part of methdoID */
444 method_offset
= msg
->method_to_find
& METHOD_MASK
;
446 /* Look up ID in hashtable and get linked list of buckets,
449 b
= inst
->data
.iftab_directptr
[ifid
& inst
->data
.hashmask
];
453 /* Found correct interface ? */
454 if (b
->InterfaceID
== ifid
)
456 /* Yep. Return method at supplied method offset */
457 ReturnPtr ("IFMeta::findmethod", struct IFMethod
*, &(b
->MethodTable
[method_offset
]));
463 /* Method not found, return NULL */
464 ReturnPtr ("IFMeta::findmethod", struct IFMethod
*, NULL
);
474 /*************************
475 ** init_ifmetaclass() **
476 *************************/
478 #define NUM_META_METHODS 5
479 #define NUM_ROOT_METHODS 1
480 BOOL
init_ifmetaclass(struct IntOOPBase
*OOPBase
)
482 struct OOP_MethodDescr root_mdescr
[NUM_ROOT_METHODS
+ 1]=
484 { (IPTR (*)())ifmeta_new
, moRoot_New
},
488 struct OOP_MethodDescr meta_mdescr
[NUM_META_METHODS
+ 1]=
490 { (IPTR (*)())ifmeta_allocdisptabs
, MO_meta_allocdisptabs
},
491 { (IPTR (*)())ifmeta_freedisptabs
, MO_meta_freedisptabs
},
492 { (IPTR (*)())ifmeta_getifinfo
, MO_meta_getifinfo
},
493 { (IPTR (*)())ifmeta_iterateifs
, MO_meta_iterateifs
},
494 { (IPTR (*)())ifmeta_findmethod
, MO_meta_findmethod
},
499 struct OOP_InterfaceDescr meta_descr
[] =
501 {root_mdescr
, IID_Root
, 1},
502 {meta_mdescr
, IID_Meta
, NUM_META_METHODS
},
506 struct ifmetaobject
*imo
= &(OOPBase
->ob_IFMetaObject
);
507 struct P_meta_allocdisptabs adt_msg
;
508 OOP_Class
*ifmeta_cl
;
510 EnterFunc(bug("init_ifmetaclass()\n"));
512 ifmeta_cl
= &(imo
->inst
.base
.public);
514 D(bug("Got ifmeta classptr\n"));
516 imo
->inst
.base
.superclass
= BASEMETAPTR
;
517 imo
->inst
.base
.public.OOPBasePtr
= (struct OOPBase
*)OOPBase
;
519 D(bug("Initialized ifmeta superclass\n"));
521 adt_msg
.superclass
= imo
->inst
.base
.superclass
;
522 adt_msg
.ifdescr
= meta_descr
;
524 /* allocdisptabs() must know the OOPBase */
525 imo
->inst
.base
.public.UserData
= (APTR
)OOPBase
;
526 /* It must also have a valid DoSuperMethod(), more exatly
527 the DoSuperMethod() of the BaseMeta class
529 imo
->inst
.base
.public.cl_DoSuperMethod
= BASEMETAPTR
->cl_DoSuperMethod
;
532 D(bug("Allocating ifmeta disptabs\n"));
535 if (ifmeta_allocdisptabs(ifmeta_cl
, (OOP_Object
*)ifmeta_cl
, &adt_msg
))
537 D(bug("ifmeta disptabs allocated\n"));
538 /* initialize Class ID */
540 imo
->inst
.base
.public.ClassNode
.ln_Name
= CLID_MIMeta
;
541 imo
->inst
.base
.public.InstOffset
= sizeof (struct metadata
);
543 D(bug("IFMeta DoMethod=%p\n", Meta_DoMethod
));
544 imo
->inst
.base
.public.cl_DoMethod
= Meta_DoMethod
;
545 imo
->inst
.base
.public.cl_CoerceMethod
= Meta_CoerceMethod
;
547 imo
->inst
.base
.instsize
= sizeof (struct ifmeta_data
);
548 imo
->inst
.base
.subclasscount
= 0UL;
549 imo
->inst
.base
.objectcount
= 0UL;
551 imo
->inst
.data
.numinterfaces
= 2UL;
553 /* This class' class is itself */
554 imo
->oclass
= &(imo
->inst
.base
.public);
558 D(bug("Trying to call get_if_info on ifmeta many times\n"));
559 for (i = 0; i < 10; i ++)
562 meta_getifinfo((OOP_Object *)imo->oclass, IID_Meta, &num_methods);
564 D(bug("IF has %ld methods\n", num_methods));
568 */ /* Make it public */
569 OOP_AddClass(ifmeta_cl
);
570 ReturnBool ("init_metaclass", TRUE
);
572 ReturnBool ("init_ifmetaclass", FALSE
);
577 /************************
578 ** calc_ht_entries() **
579 ************************/
581 /* Calculates the number of interfaces the new class has
582 ( == number of buckets in the hashtable)
584 static ULONG
calc_ht_entries(struct ifmeta_inst
*cl
586 ,struct OOP_InterfaceDescr
*ifDescr
587 ,struct IntOOPBase
*OOPBase
)
591 EnterFunc(bug("calc_ht_entries(cl=%p, ifDescr=%p, super=%p)\n", cl
, ifDescr
, super
));
597 /* Get number of interfaces (method tables) in superclass */
600 num_if
= MD(super
)->numinterfaces
;
602 D(bug("Super-interfaces: %ld\n", num_if
));
604 /* Check if there are any new interfaces in this class */
606 for (; ifDescr
->MethodTable
; ifDescr
++)
608 struct P_meta_getifinfo gii_msg
;
611 D(bug("Checking for interface %s\n", ifDescr
->InterfaceID
));
613 gii_msg
.mid
= OOP_GetMethodID(IID_Meta
, MO_meta_getifinfo
);
614 gii_msg
.interface_id
= ifDescr
->InterfaceID
;
615 gii_msg
.num_methods_ptr
= &num_methods
;
618 /* Does super support interface ? */
619 D(bug("Calling CoerceMethod on class %s\n", OOP_OCLASS(super
)->ClassNode
.ln_Name
));
620 if (!OOP_CoerceMethod(OOP_OCLASS(super
), (OOP_Object
*)super
, (OOP_Msg
)&gii_msg
))
622 D(bug("Found new interface: %s\n", ifDescr
->InterfaceID
));
624 /* If it didn't then we have a new interface for this class */
628 } /* for (each interface in the description for the class) */
633 /* This is a baseclass, count the interfaces */
634 for (; ifDescr
->MethodTable
; ifDescr
++)
638 } /* for (each interface in the description for the class) */
640 ReturnInt ("calc_ht_entries", ULONG
, num_if
);
648 /*********************
650 *********************/
651 /* Creates a new interface bucket */
652 static struct IFBucket
*createbucket(
655 ,struct IntOOPBase
*OOPBase
)
657 struct IFMethod
*ifm
= NULL
;
658 ULONG mtab_size
= UB (&ifm
[num_methods
]) - UB( &ifm
[0]);
660 /* Allocate bucket */
661 struct IFBucket
*ifb
;
663 ifb
= (struct IFBucket
*)AllocMem( sizeof (struct IFBucket
), MEMF_ANY
);
666 /* Allocate method table for this interface */
667 ifb
->MethodTable
= (struct IFMethod
*)AllocVec(mtab_size
, MEMF_ANY
);
668 if (ifb
->MethodTable
)
670 /* Get correct ID for the interface (string ID => interface ID mapping) */
672 if (init_mi_methodbase(interface_id
, &(ifb
->InterfaceID
), OOPBase
))
674 /* Save number of methods in the interface */
675 ifb
->NumMethods
= num_methods
;
677 /* Save the global string representations of the ID */
678 ifb
->GlobalInterfaceID
= interface_id
;
683 FreeMem (ifb
, sizeof (struct IFBucket
));
689 /***********************
690 ** Hash table hooks **
691 ***********************/
692 #define IB(x) ((struct IFBucket *)x)
694 VOID
freebucket(struct Bucket
*b
, struct IntOOPBase
*OOPBase
)
697 /* Free methodtable */
698 FreeVec(IB(b
)->MethodTable
);
700 /* Free the bucket itself */
701 FreeMem(b
, sizeof (struct IFBucket
));
707 /* Copies a hashtable bucket */
708 struct Bucket
*copyBucket(struct Bucket
*old_b
, APTR data
, struct IntOOPBase
*OOPBase
)
710 struct IFBucket
*new_b
;
712 EnterFunc(bug("CopyBucket(old_b=%p)\n", old_b
));
714 /* Allocate memory for the new interface bucket */
715 new_b
= (struct IFBucket
*)AllocMem(sizeof (struct IFBucket
), MEMF_ANY
);
718 struct IFMethod
*ifm
= NULL
;
721 /* Get number of methods in source methodtable */
722 ULONG numentries
= IB(old_b
)->NumMethods
;
724 mtab_size
= UB(&ifm
[numentries
]) - UB(&ifm
[0]);
726 /* Allocate memory for methodtable of same size as source one */
727 new_b
->MethodTable
= (struct IFMethod
*)AllocVec(mtab_size
, MEMF_ANY
);
728 if (new_b
->MethodTable
)
730 /* Copy methodtable to destination */
731 CopyMem(IB(old_b
)->MethodTable
, new_b
->MethodTable
, mtab_size
);
733 /* Initialize bucket */
734 new_b
->InterfaceID
= IB(old_b
)->InterfaceID
;
735 new_b
->NumMethods
= IB(old_b
)->NumMethods
;
736 new_b
->GlobalInterfaceID
= IB(old_b
)->GlobalInterfaceID
;
738 ReturnPtr ("CopyBucket", struct Bucket
*, (struct Bucket
*)new_b
);
740 FreeMem (new_b
, sizeof (struct IFBucket
));
743 ReturnPtr ("CopyBucket", struct Bucket
*, NULL
);
748 /* Default function for calling DoMethod() on a local object */
753 #define OOPBase ((struct IntOOPBase *)OOP_OOPBASE(object))
755 static IPTR
Meta_DoMethod(OOP_Object
*object
, OOP_Msg msg
)
757 struct metadata
*cl
= (struct metadata
*)OOP_OCLASS(object
);
760 /* Macro below defined in intern.h */
761 IntCallMethod(cl
, object
, msg
);
769 static IPTR
Meta_CoerceMethod(OOP_Class
*cl
, OOP_Object
*object
, OOP_Msg msg
)
771 IntCallMethod(cl
, object
, msg
);
774 /********************
776 ********************/
777 static IPTR
Meta_DoSuperMethod(OOP_Class
*cl
, OOP_Object
*object
, OOP_Msg msg
)
779 cl
= IFI(cl
)->base
.superclass
;
780 IntCallMethod(cl
, object
, msg
);