// mono extern "C" { #include #include #include #include #include #include #include // we need this one - it's in mono-devel gpointer mono_delegate_to_ftnptr (MonoDelegate *delegate); } #ifdef _MSC_VER #pragma warning(disable: 4091) #endif #include #include #include #include #ifdef _WIN32 #include // for _close #define close _close #else #include #endif #include #include #include #define CALL __stdcall // main class library #define CORELIB "PureData" #define DLLEXT "dll" // symbol for inter-object messages #define SYM_OBJECT "clr-object" static t_symbol *sym_object; struct PassedData { MonoObject object; MonoObject *klass; MonoObject *ext; MonoObject *obj; }; struct AtomList { AtomList(int c,t_atom *v): argc(c),argv(v) {} int argc; t_atom *argv; }; // cached mono data static MonoDomain *monodomain; static PassedData *clr_pass; // transforms a pointer (like MonoObject *) into a atom list template struct ObjectAtom { // 64-bit safe... enum { bitshift = 22,size = sizeof(T)*8/bitshift+1 }; t_atom msg[size]; ObjectAtom() {} ObjectAtom(T o) { long ptr = (long)o; for(int i = 0; i < size; ++i) { SETFLOAT(msg+i,(int)(ptr&((1<>= bitshift; } } static bool check(int argc,const t_atom *argv) { if(argc != size) return false; for(int i = 0; i < size; ++i) if(argv[i].a_type != A_FLOAT) return false; return true; } static T ptr(const t_atom *argv) { long ret = 0; for(int i = size-1; i >= 0; --i) ret = (ret< ClrMap; // this holds the class name to class structure association static ClrMap clr_map; typedef std::vector OutletArr; typedef std::list ProxyList; // this is the class to be setup (while we are in the CLR static Main method) static t_clr_class *clr_setup_class = NULL; // inlet index... must start with 0 every time a new object is made static int clr_inlet; // this is the class instance object structure struct t_instance { t_object pd_obj; // myself t_clr_class *pd_class; MonoObject *mono_obj; // the mono class instance guint32 mono_handle; // the mono class instance OutletArr *outlets; ProxyList *proxies; }; typedef void (__stdcall NewClass)(t_clr_class *c,const t_symbol *sym); typedef void (__stdcall NewInstance)(t_instance *p,AtomList l); typedef void (__stdcall CallBang)(); typedef void (__stdcall CallFloat)(float f); typedef void (__stdcall CallSymbol)(const t_symbol *sym); typedef void (__stdcall CallPointer)(const t_gpointer *ptr); typedef void (__stdcall CallList)(AtomList l); typedef void (__stdcall CallAnything)(int inlet,const t_symbol *sym,AtomList l); typedef void (__stdcall CallObject)(int inlet); static NewClass *newclass = NULL; static NewInstance *newinstance = NULL; static CallBang *callbang = NULL; static CallFloat *callfloat = NULL; static CallSymbol *callsymbol = NULL; static CallPointer *callpointer = NULL; static CallList *calllist = NULL; static CallAnything *callanything = NULL; static CallObject *callobject = NULL; #if 0 // Print error message given by exception static void error_exc(char* txt,char* cname,MonoObject* exc) { MonoMethod* m = mono_method_desc_search_in_class(clr_desc_tostring,mono_get_exception_class()); assert(m); m = mono_object_get_virtual_method(exc,m); assert(m); MonoString* str = (MonoString*)mono_runtime_invoke(m,exc,NULL,NULL); assert(str); error("CLR class %s: %s",txt,cname); error(mono_string_to_utf8(str)); } #endif static void clr_method_bang(t_instance *x) { assert(x); assert(callbang); clr_pass->ext = x->mono_obj; callbang(); } static void clr_method_float(t_instance *x,t_float f) { assert(x); assert(callfloat); clr_pass->ext = x->mono_obj; callfloat(f); } static void clr_method_symbol(t_instance *x,t_symbol *s) { assert(x); assert(callsymbol); clr_pass->ext = x->mono_obj; callsymbol(s); } static void clr_method_pointer(t_instance *x,t_gpointer *p) { assert(x); assert(callpointer); clr_pass->ext = x->mono_obj; callpointer(p); } static void clr_method_list(t_instance *x,t_symbol *,int argc,t_atom *argv) { assert(x); assert(calllist); clr_pass->ext = x->mono_obj; calllist(AtomList(argc,argv)); } static void call_anything(t_instance *x,int inlet,t_symbol *s,int argc,t_atom *argv) { assert(x); clr_pass->ext = x->mono_obj; if(s == sym_object && ObjectAtom::check(argc,argv)) { assert(callobject); guint32 hnd = ObjectAtom::ptr(argv); clr_pass->obj = mono_gchandle_get_target(hnd); callobject(inlet); } else { assert(callanything); callanything(inlet,s,AtomList(argc,argv)); } } static void clr_method_anything(t_instance *x,t_symbol *s,int argc,t_atom *argv) { call_anything(x,0,s,argc,argv); } static void clr_method_proxy(t_proxy *x,t_symbol *s,int argc,t_atom *argv) { call_anything(x->parent,x->inlet,s,argc,argv); } static void CALL PD_Post(MonoString *str) { post("%s",mono_string_to_utf8(str)); } static void CALL PD_PostError(MonoString *str) { error("%s",mono_string_to_utf8(str)); } static void CALL PD_PostVerbose(int lvl,MonoString *str) { verbose(lvl,"%s",mono_string_to_utf8(str)); } static void *CALL PD_SymGen(MonoString *str) { assert(str); return gensym(mono_string_to_utf8(str)); } static MonoString *CALL PD_SymEval(t_symbol *sym) { assert(sym); return mono_string_new(monodomain,sym->s_name); } static void CALL PD_AddInletAlias(t_instance *obj,t_symbol *sel,t_symbol *to_sel) { ++clr_inlet; assert(obj); t_inlet *in = inlet_new(&obj->pd_obj,&obj->pd_obj.ob_pd,sel,to_sel); assert(in); } static void CALL PD_AddInletFloat(t_instance *obj,float *f) { ++clr_inlet; assert(obj); t_inlet *in = floatinlet_new(&obj->pd_obj,f); assert(in); } static void CALL PD_AddInletSymbol(t_instance *obj,t_symbol **s) { ++clr_inlet; assert(obj); t_inlet *in = symbolinlet_new(&obj->pd_obj,s); assert(in); } /* static void CALL PD_AddInletPointer(t_clr *obj,t_gpointer *p) { ++clr_inlet; assert(obj); t_inlet *in = pointerinlet_new(&obj->pd_obj,p); assert(in); } */ static void CALL PD_AddInletProxyTyped(t_instance *obj,t_symbol *type) { assert(obj); t_proxy *p = (t_proxy *)pd_new(proxy_class); p->parent = obj; p->inlet = ++clr_inlet; if(!obj->proxies) obj->proxies = new ProxyList; obj->proxies->push_back(p); t_inlet *in = inlet_new(&obj->pd_obj,&p->pd_obj.ob_pd,type,type); assert(in); } static void CALL PD_AddInletProxy(t_instance *obj) { PD_AddInletProxyTyped(obj,NULL); } static void CALL PD_AddOutlet(t_instance *obj,t_symbol *type) { assert(obj); t_outlet *out = outlet_new(&obj->pd_obj,type); assert(out); if(!obj->outlets) obj->outlets = new OutletArr; obj->outlets->push_back(out); } static void CALL PD_OutletBang(t_instance *obj,int n) { assert(obj); assert(obj->outlets); assert(n >= 0 && n < (int)obj->outlets->size()); outlet_bang((*obj->outlets)[n]); } static void CALL PD_OutletFloat(t_instance *obj,int n,float f) { assert(obj); assert(obj->outlets); assert(n >= 0 && n < (int)obj->outlets->size()); outlet_float((*obj->outlets)[n],f); } static void CALL PD_OutletSymbol(t_instance *obj,int n,t_symbol *s) { assert(obj); assert(obj->outlets); assert(n >= 0 && n < (int)obj->outlets->size()); outlet_symbol((*obj->outlets)[n],s); } static void CALL PD_OutletPointer(t_instance *obj,int n,t_gpointer *p) { assert(obj); assert(obj->outlets); assert(n >= 0 && n < (int)obj->outlets->size()); outlet_pointer((*obj->outlets)[n],p); } static void CALL PD_OutletAtom(t_instance *obj,int n,t_atom l) { assert(obj); assert(obj->outlets); assert(n >= 0 && n < (int)obj->outlets->size()); t_outlet* out = (*obj->outlets)[n]; switch(l.a_type) { case A_FLOAT: outlet_float(out,l.a_w.w_float); break; case A_SYMBOL: outlet_symbol(out,l.a_w.w_symbol); break; case A_POINTER: outlet_pointer(out,l.a_w.w_gpointer); break; default: assert(false); } } static void CALL PD_OutletAnything(t_instance *obj,int n,t_symbol *s,MonoArray *l) { assert(obj); assert(obj->outlets); assert(n >= 0 && n < (int)obj->outlets->size()); // assert(mono_object_get_class(&l->obj) == clr_atom); outlet_anything((*obj->outlets)[n],s,mono_array_length(l),mono_array_addr(l,t_atom,0)); } static void CALL PD_OutletObject(t_instance *obj,int n,MonoObject *o) { assert(obj); assert(obj->outlets); assert(n >= 0 && n < (int)obj->outlets->size()); guint32 hnd = mono_gchandle_new(o,TRUE); ObjectAtom oa(hnd); outlet_anything((*obj->outlets)[n],sym_object,oa.size,oa); mono_gchandle_free(hnd); } static void CALL PD_SendAtom(t_symbol *dst,t_atom a) { void *cl = dst->s_thing; if(cl) pd_forwardmess((t_class **)cl,1,&a); } static void CALL PD_SendAnything(t_symbol *dst,t_symbol *s,MonoArray *l) { void *cl = dst->s_thing; // assert(mono_object_get_class(&l->obj) == clr_atom); if(cl) pd_typedmess((t_class **)cl,s,mono_array_length(l),mono_array_addr(l,t_atom,0)); } static void CALL PD_SendObject(t_symbol *dst,MonoObject *o) { void *cl = dst->s_thing; // assert(mono_object_get_class(&l->obj) == clr_atom); if(cl) { guint32 hnd = mono_gchandle_new(o,TRUE); ObjectAtom oa(hnd); pd_typedmess((t_class **)cl,sym_object,oa.size,oa); mono_gchandle_free(hnd); } } static MonoString *CALL PD_SearchPath(MonoString *file) { char *filestr = mono_string_to_utf8(file); char dirbuf[MAXPDSTRING],*nameptr; // search for classname.dll in the PD path int fd; if ((fd = open_via_path("",filestr,"",dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0) { // found if(dirbuf != nameptr) { // fix for the fact that open_via_path doesn't return a path, when it's found in . strcat(dirbuf,"/"); strcat(dirbuf,filestr); // close(fd); // strange - we get an assertion failure here... } return mono_string_new(monodomain,dirbuf); } else return mono_string_new(monodomain,""); } void *clr_new(t_symbol *classname, int argc, t_atom *argv) { // find class name in map ClrMap::iterator it = clr_map.find(classname); if(it == clr_map.end()) { error("CLR - class %s not found",classname->s_name); return NULL; } t_clr_class *clss = it->second; // make instance t_instance *x = (t_instance *)pd_new(clss->pd_class); x->pd_class = clss; x->outlets = NULL; x->proxies = NULL; clr_inlet = 0; assert(newinstance); clr_pass->klass = clss->mono_class; newinstance(x,AtomList(argc,argv)); x->mono_obj = clr_pass->ext; #ifdef _DEBUG clr_pass->klass = NULL; #endif if(x->mono_obj) { x->mono_handle = mono_gchandle_new(x->mono_obj,TRUE); return x; } else { pd_free((t_pd *)x); error("CLR - class %s could not be instantiated",classname->s_name); return NULL; } } void clr_free(t_instance *obj) { mono_gchandle_free(obj->mono_handle); if(obj->outlets) delete obj->outlets; if(obj->proxies) { for(ProxyList::iterator it = obj->proxies->begin(); it != obj->proxies->end(); ++it) pd_free((t_pd *)*it); delete obj->proxies; } } static void CALL PD_Register(MonoDelegate *d_class,MonoDelegate *d_new,MonoDelegate *d_bang,MonoDelegate *d_float,MonoDelegate *d_symbol,MonoDelegate *d_pointer,MonoDelegate *d_list,MonoDelegate *d_anything,MonoDelegate *d_object) { newclass = (NewClass *)mono_delegate_to_ftnptr(d_class); newinstance = (NewInstance *)mono_delegate_to_ftnptr(d_new); callbang = (CallBang *)mono_delegate_to_ftnptr(d_bang); callfloat = (CallFloat *)mono_delegate_to_ftnptr(d_float); callsymbol = (CallSymbol *)mono_delegate_to_ftnptr(d_symbol); callpointer = (CallPointer *)mono_delegate_to_ftnptr(d_pointer); calllist = (CallList *)mono_delegate_to_ftnptr(d_list); callanything = (CallAnything *)mono_delegate_to_ftnptr(d_anything); callobject = (CallObject *)mono_delegate_to_ftnptr(d_object); } static bool CALL PD_RegisterClass(t_clr_class *c,t_symbol *classsym,int classflags,int methodflags) { assert(c && !c->pd_class); c->pd_class = class_new(classsym,(t_newmethod)clr_new,(t_method)clr_free, sizeof(t_instance), classflags, A_GIMME, A_NULL); if(c->pd_class) { if(methodflags&0x01) class_addbang(c->pd_class,clr_method_bang); if(methodflags&0x02) class_addfloat(c->pd_class,clr_method_float); if(methodflags&0x04) class_addsymbol(c->pd_class,clr_method_symbol); if(methodflags&0x08) class_addpointer(c->pd_class,clr_method_pointer); if(methodflags&0x10) class_addlist(c->pd_class,clr_method_list); if(methodflags&0x20) class_addanything(c->pd_class,clr_method_anything); return true; } else return false; } static int classloader(char *dirname, char *classname, char *altname) { if(!newclass) { post("CLR - Entry point not set"); return 0; } t_symbol *classsym = gensym(classname); t_clr_class *classdef = (t_clr_class *)getbytes(sizeof(t_clr_class)); // set all struct members to 0 memset(classdef,0,sizeof(t_clr_class)); newclass(classdef,classsym); classdef->mono_class = clr_pass->klass; if(!classdef->mono_class) { freebytes(classdef,sizeof(t_clr_class)); return 0; } mono_gchandle_new(classdef->mono_class,TRUE); // we don't remember the handle... won't be freed later // put into map clr_map[classsym] = classdef; verbose(1,"CLR - Loaded class %s OK",classname); return 1; } extern "C" #ifdef _MSC_VER __declspec(dllexport) #endif void clr_setup(void) { #ifdef _WIN32 // set mono paths const char *monopath = getenv("MONO_PATH"); if(!monopath) { error("CLR - Please set the MONO_PATH environment variable to the folder of your MONO installation - CLR not loaded!"); return; } char tlib[256],tconf[256]; strcpy(tlib,monopath); strcat(tlib,"/lib"); strcpy(tconf,monopath); strcat(tconf,"/etc"); mono_set_dirs(tlib,tconf); #endif // try to find PureData.dll in the PD path char dirbuf[MAXPDSTRING],*nameptr; // search in the PD path int fd; if ((fd = open_via_path("",CORELIB,"." DLLEXT,dirbuf,&nameptr,MAXPDSTRING,1)) >= 0) { if(dirbuf != nameptr) // fix for the fact that open_via_path doesn't return a path, when it's found in . strcat(dirbuf,"/" CORELIB "." DLLEXT); // close(fd); // strange - we get an assertion failure here... } else { error("CLR - " CORELIB "." DLLEXT " not found in path"); return; } try { monodomain = mono_jit_init(dirbuf); } catch(...) { monodomain = NULL; } if(monodomain) { // look for PureData.dll MonoAssembly *assembly = mono_domain_assembly_open (monodomain,dirbuf); if(!assembly) { error("CLR - assembly %s not found!",dirbuf); return; } MonoImage *image = mono_assembly_get_image(assembly); assert(image); // add mono to C hooks mono_add_internal_call("PureData.Internal::SymGen(string)", (const void *)PD_SymGen); mono_add_internal_call("PureData.Internal::SymEval(PureData.Symbol)", (const void *)PD_SymEval); mono_add_internal_call("PureData.Public::Post(string)",(const void *)PD_Post); mono_add_internal_call("PureData.Public::PostError(string)",(const void *)PD_PostError); mono_add_internal_call("PureData.Public::PostVerbose(int,string)",(const void *)PD_PostVerbose); mono_add_internal_call("PureData.Public::SearchPath(string)",(const void *)PD_SearchPath); mono_add_internal_call("PureData.Internal::AddInlet(PureData.ExternalPtr,PureData.Symbol,PureData.Symbol)", (const void *)PD_AddInletAlias); mono_add_internal_call("PureData.Internal::AddInlet(PureData.ExternalPtr,single&)", (const void *)PD_AddInletFloat); mono_add_internal_call("PureData.Internal::AddInlet(PureData.ExternalPtr,PureData.Symbol&)", (const void *)PD_AddInletSymbol); // mono_add_internal_call("PureData.Internal::AddInlet(PureData.ExternalPtr,PureData.Pointer&)", (const void *)PD_AddInletPointer); // mono_add_internal_call("PureData.Internal::AddInlet(PureData.ExternalPtr,PureData.Symbol)", (const void *)PD_AddInletTyped); mono_add_internal_call("PureData.Internal::AddInlet(PureData.ExternalPtr)", (const void *)PD_AddInletProxy); mono_add_internal_call("PureData.Internal::AddOutlet(PureData.ExternalPtr,PureData.Symbol)", (const void *)PD_AddOutlet); mono_add_internal_call("PureData.Internal::Outlet(PureData.ExternalPtr,int)", (const void *)PD_OutletBang); mono_add_internal_call("PureData.Internal::Outlet(PureData.ExternalPtr,int,single)", (const void *)PD_OutletFloat); mono_add_internal_call("PureData.Internal::Outlet(PureData.ExternalPtr,int,PureData.Symbol)", (const void *)PD_OutletSymbol); mono_add_internal_call("PureData.Internal::Outlet(PureData.ExternalPtr,int,PureData.Pointer)", (const void *)PD_OutletPointer); mono_add_internal_call("PureData.Internal::Outlet(PureData.ExternalPtr,int,PureData.Atom)", (const void *)PD_OutletAtom); mono_add_internal_call("PureData.Internal::Outlet(PureData.ExternalPtr,int,PureData.Symbol,PureData.Atom[])", (const void *)PD_OutletAnything); mono_add_internal_call("PureData.Internal::OutletEx(PureData.ExternalPtr,int,object)", (const void *)PD_OutletObject); // mono_add_internal_call("PureData.Internal::Bind(PureData.ExternalPtr,PureData.Symbol)", (const void *)PD_Bind); // mono_add_internal_call("PureData.Internal::Unbind(PureData.ExternalPtr,PureData.Symbol)", (const void *)PD_Unbind); mono_add_internal_call("PureData.Class::Register(PureData.Class/DelegateClass,PureData.Class/DelegateNew,PureData.Class/DelegateBang,PureData.Class/DelegateFloat,PureData.Class/DelegateSymbol,PureData.Class/DelegatePointer,PureData.Class/DelegateList,PureData.Class/DelegateAnything,PureData.Class/DelegateObject)", (const void *)PD_Register); mono_add_internal_call("PureData.Class::RegisterClass(PureData.ClassPtr,PureData.Symbol,PureData.Public/ClassType,PureData.Class/MethodFlags)", (const void *)PD_RegisterClass); // mono_add_internal_call("PureData.Class::AddMethodIntern(PureData.ClassPtr,int,PureData.Symbol,PureData.Class/Kind,System.Delegate)", (const void *)PD_AddMethodIntern); mono_add_internal_call("PureData.External::Send(PureData.Symbol,PureData.Atom)", (const void *)PD_SendAtom); mono_add_internal_call("PureData.External::Send(PureData.Symbol,PureData.Symbol,PureData.Atom[])", (const void *)PD_SendAnything); mono_add_internal_call("PureData.External::SendEx(PureData.Symbol,object)", (const void *)PD_SendObject); /////////////////////////////////////////////////////////// // make proxy class proxy_class = class_new(gensym("clr proxy"),NULL,NULL,sizeof(t_proxy),CLASS_PD|CLASS_NOINLET,A_NULL); class_addanything(proxy_class,clr_method_proxy); // symbol for Mono object handling sym_object = gensym(SYM_OBJECT); // install loader hook sys_register_loader(classloader); /////////////////////////////////////////////////////////// // call static Class::Setup function MonoClass *clr_class = mono_class_from_name(image,"PureData","Class"); assert(clr_class); MonoMethodDesc *desc = mono_method_desc_new("::Setup()",FALSE); MonoMethod *setup = mono_method_desc_search_in_class(desc,clr_class); MonoObject *exc; mono_runtime_invoke(setup,NULL,NULL,&exc); if(exc) post("CLR - setup function failed"); else { // ready! post("CLR extension - (c)2006 Davide Morelli, Thomas Grill"); } // necessary data should have been set by Setup MonoClass *clr_internal = mono_class_from_name(image,"PureData","Internal"); assert(clr_internal); MonoVTable *clr_internal_vt = mono_class_vtable(monodomain,clr_internal); MonoClassField *fld = mono_class_get_field_from_name (clr_internal,"pass"); assert(fld); mono_field_static_get_value(clr_internal_vt,fld,&clr_pass); assert(clr_pass); } else error("clr: mono domain couldn't be initialized!"); }