diff --git a/Examples/test-suite/ruby/director_basic_runme.rb b/Examples/test-suite/ruby/director_basic_runme.rb new file mode 100644 index 000000000..303b08501 --- /dev/null +++ b/Examples/test-suite/ruby/director_basic_runme.rb @@ -0,0 +1,18 @@ +require 'director_basic' + +class MyFoo < Director_basic::Foo + def ping + "MyFoo::ping()" + end +end + +a = MyFoo.new + +raise RuntimeError if a.ping != "MyFoo::ping()" +raise RuntimeError if a.pong != "Foo::pong();MyFoo::ping()" + +b = Director_basic::Foo.new + +raise RuntimeError if b.ping != "Foo::ping()" +raise RuntimeError if b.pong != "Foo::pong();Foo::ping()" + diff --git a/Source/Modules/ruby.cxx b/Source/Modules/ruby.cxx index 79dc98a8c..4f7278789 100644 --- a/Source/Modules/ruby.cxx +++ b/Source/Modules/ruby.cxx @@ -22,7 +22,252 @@ char cvsroot_ruby_cxx[] = "$Header$"; #include #include /* for INT_MAX */ -class RClass { +/********************************************************************************** + * + * TYPE UTILITY FUNCTIONS + * + * These ultimately belong elsewhere, but for now I'm leaving them here to + * make it easier to keep an eye on them during testing. Not all of these may + * be necessary, and some may duplicate existing functionality in SWIG. --MR + * + **********************************************************************************/ + +/* Swig_csuperclass_call() + * + * Generates a fully qualified method call, including the full parameter list. + * e.g. "base::method(i, j)" + * + */ + +static String *Swig_csuperclass_call(String* base, String* method, ParmList* l) { + String *call = NewString(""); + Parm *p; + if (base) { + Printf(call, "%s::", base); + } + Printf(call, "%s(", method); + for (p=l; p; p = nextSibling(p)) { + String *pname = Getattr(p, "name"); + if (p != l) Printf(call, ", "); + Printv(call, pname, NIL); + } + Printf(call, ")"); + return call; +} + +/* Swig_class_declaration() + * + * Generate the start of a class/struct declaration. + * e.g. "class myclass" + * + */ + +static String *Swig_class_declaration(Node *n, String *name) { + if (!name) { + name = Getattr(n, "sym:name"); + } + String *result = NewString(""); + String *kind = Getattr(n, "kind"); + Printf(result, "%s %s", kind, name); + return result; +} + +static String *Swig_class_name(Node *n) { + String *name; + name = Copy(Getattr(n, "sym:name")); + return name; +} + +/* Swig_director_declaration() + * + * Generate the full director class declaration, complete with base classes. + * e.g. "class __DIRECTOR__myclass: public myclass, public __DIRECTOR__ {" + * + */ + +static String *Swig_director_declaration(Node *n) { + String* classname = Swig_class_name(n); + String *directorname = NewStringf("__DIRECTOR__%s", classname); + String *base = Getattr(n, "classtype"); + String *declaration = Swig_class_declaration(n, directorname); + Printf(declaration, " : public %s, public __DIRECTOR__ {\n", base); + Delete(classname); + Delete(directorname); + return declaration; +} + + +static String *Swig_method_call(String_or_char *name, ParmList *parms) { + String *func; + int i = 0; + int comma = 0; + Parm *p = parms; + SwigType *pt; + String *nname; + + func = NewString(""); + nname = SwigType_namestr(name); + Printf(func,"%s(", nname); + while (p) { + String *pname; + pt = Getattr(p,"type"); + if ((SwigType_type(pt) != T_VOID)) { + if (comma) Printf(func,","); + pname = Getattr(p, "name"); + Printf(func,"%s", pname); + comma = 1; + i++; + } + p = nextSibling(p); + } + Printf(func,")"); + return func; +} + +/* method_decl + * + * Misnamed and misappropriated! Taken from SWIG's type string manipulation utilities + * and modified to generate full (or partial) type qualifiers for method declarations, + * local variable declarations, and return value casting. More importantly, it merges + * parameter type information with actual parameter names to produce a complete method + * declaration that fully mirrors the original method declaration. + * + * There is almost certainly a saner way to do this. + * + * This function needs to be cleaned up and possibly split into several smaller + * functions. For instance, attaching default names to parameters should be done in a + * separate function. + * + */ + +static String *method_decl(SwigType *s, const String_or_char *id, List *args, int strip, int values) { + String *result; + List *elements; + String *element = 0, *nextelement; + int is_const = 0; + int nelements, i; + int is_func = 0; + int arg_idx = 0; + + if (id) { + result = NewString(Char(id)); + } else { + result = NewString(""); + } + + elements = SwigType_split(s); + nelements = Len(elements); + if (nelements > 0) { + element = Getitem(elements, 0); + } + for (i = 0; i < nelements; i++) { + if (i < (nelements - 1)) { + nextelement = Getitem(elements, i+1); + } else { + nextelement = 0; + } + if (SwigType_isqualifier(element)) { + int skip = 0; + DOH *q = 0; + if (!strip) { + q = SwigType_parm(element); + if (!Cmp(q, "const")) { + is_const = 1; + is_func = SwigType_isfunction(nextelement); + if (is_func) skip = 1; + skip = 1; + } + if (!skip) { + Insert(result,0," "); + Insert(result,0,q); + } + Delete(q); + } + } else if (SwigType_ispointer(element)) { + Insert(result,0,"*"); + if ((nextelement) && ((SwigType_isfunction(nextelement) || (SwigType_isarray(nextelement))))) { + Insert(result,0,"("); + Append(result,")"); + } + } else if (SwigType_ismemberpointer(element)) { + String *q; + q = SwigType_parm(element); + Insert(result,0,"::*"); + Insert(result,0,q); + if ((nextelement) && ((SwigType_isfunction(nextelement) || (SwigType_isarray(nextelement))))) { + Insert(result,0,"("); + Append(result,")"); + } + Delete(q); + } + else if (SwigType_isreference(element)) { + Insert(result,0,"&"); + } else if (SwigType_isarray(element)) { + DOH *size; + Append(result,"["); + size = SwigType_parm(element); + Append(result,size); + Append(result,"]"); + Delete(size); + } else if (SwigType_isfunction(element)) { + Parm *parm; + String *p; + Append(result,"("); + parm = args; + while (parm != 0) { + String *type = Getattr(parm, "type"); + String* name = Getattr(parm, "name"); + if (!name && Cmp(type, "void")) { + name = NewString(""); + Printf(name, "arg%d", arg_idx++); + Setattr(parm, "name", name); + } + if (!name) { + name = NewString(""); + } + p = SwigType_str(type, name); + Append(result,p); + String* value = Getattr(parm, "value"); + if (values && (value != 0)) { + Printf(result, " = %s", value); + } + parm = nextSibling(parm); + if (parm != 0) Append(result,", "); + } + Append(result,")"); + } else { + if (Strcmp(element,"v(...)") == 0) { + Insert(result,0,"..."); + } else { + String *bs = SwigType_namestr(element); + Insert(result,0," "); + Insert(result,0,bs); + Delete(bs); + } + } + element = nextelement; + } + Delete(elements); + if (is_const) { + if (is_func) { + Append(result, " "); + Append(result, "const"); + } else { + Insert(result, 0, "const "); + } + } + Chop(result); + return result; +} + + +/********************************************************************************** + * + * END OF TYPE UTILITY FUNCTIONS + * + **********************************************************************************/ + + class RClass { private: String *temp; public: @@ -142,10 +387,15 @@ private: Hash *classes; /* key=cname val=RClass */ RClass *klass; /* Currently processing class */ Hash *special_methods; /* Python style special method name table */ + + File *f_directors; + File *f_directors_h; File *f_runtime; + File *f_runtime_h; File *f_header; File *f_wrappers; File *f_init; + bool use_kw; bool useGlobalModule; bool multipleInheritance; @@ -326,6 +576,9 @@ public: if (swigModule) { Node *options = Getattr(swigModule, "options"); if (options) { + if (Getattr(options, "directors")) { + allow_directors(); + } if (Getattr(options, "ruby_globalmodule")) { useGlobalModule = true; } @@ -335,23 +588,41 @@ public: } } + /* Set comparison with none for ConstructorToFunction */ + // setSubclassInstanceCheck(NewString("CLASS_OF(self) != Qnil")); + setSubclassInstanceCheck(NewString("CLASS_OF(self) != cFoo.klass")); + /* Initialize all of the output files */ String *outfile = Getattr(n,"outfile"); + String *outfile_h = Getattr(n, "outfile_h"); f_runtime = NewFile(outfile,"w"); if (!f_runtime) { Printf(stderr,"*** Can't open '%s'\n", outfile); SWIG_exit(EXIT_FAILURE); } + + if (directorsEnabled()) { + f_runtime_h = NewFile(outfile_h,"w"); + if (!f_runtime_h) { + Printf(stderr,"*** Can't open '%s'\n", outfile_h); + SWIG_exit(EXIT_FAILURE); + } + } + f_init = NewString(""); f_header = NewString(""); f_wrappers = NewString(""); + f_directors_h = NewString(""); + f_directors = NewString(""); /* Register file targets with the SWIG file handler */ Swig_register_filebyname("header",f_header); Swig_register_filebyname("wrapper",f_wrappers); Swig_register_filebyname("runtime",f_runtime); Swig_register_filebyname("init",f_init); + Swig_register_filebyname("director",f_directors); + Swig_register_filebyname("director_h",f_directors_h); modvar = 0; current = NO_CPP; @@ -366,6 +637,10 @@ public: Printf(f_runtime, "#define SWIG_NOINCLUDE\n"); } + if (directorsEnabled()) { + Printf(f_runtime,"#define SWIG_DIRECTORS\n"); + } + /* typedef void *VALUE */ SwigType *value = NewSwigType(T_VOID); SwigType_add_pointer(value); @@ -375,6 +650,19 @@ public: /* Set module name */ set_module(Char(Getattr(n,"name"))); + if (directorsEnabled()) { + Swig_banner(f_directors_h); + Printf(f_directors_h, "#ifndef __%s_WRAP_H__\n", module); + Printf(f_directors_h, "#define __%s_WRAP_H__\n\n", module); + Printf(f_directors_h, "class __DIRECTOR__;\n\n"); + Swig_insert_file("director.swg", f_directors); + Printf(f_directors, "\n\n"); + Printf(f_directors, "/* ---------------------------------------------------\n"); + Printf(f_directors, " * C++ director class methods\n"); + Printf(f_directors, " * --------------------------------------------------- */\n\n"); + Printf(f_directors, "#include \"%s\"\n\n", outfile_h); + } + Printf(f_header,"#define SWIG_init Init_%s\n", feature); Printf(f_header,"#define SWIG_name \"%s\"\n\n", module); Printf(f_header,"static VALUE %s;\n", modvar); @@ -412,8 +700,18 @@ public: /* Close all of the files */ Dump(f_header,f_runtime); + + if (directorsEnabled()) { + Dump(f_directors, f_runtime); + Dump(f_directors_h, f_runtime_h); + Printf(f_runtime_h, "\n"); + Printf(f_runtime_h, "#endif /* __%s_WRAP_H__ */\n", module); + Close(f_runtime_h); + } + Dump(f_wrappers,f_runtime); Wrapper_pretty_print(f_init,f_runtime); + Delete(f_header); Delete(f_wrappers); Delete(f_init); @@ -600,13 +898,17 @@ public: * the function wrapper. * --------------------------------------------------------------------- */ - void marshalInputArgs(ParmList *l, int numarg, int numreq, int start, String *kwargs, bool allow_kwargs, Wrapper *f) { + void marshalInputArgs(Node *n, ParmList *l, int numarg, int numreq, int start, String *kwargs, bool allow_kwargs, Wrapper *f) { int i; Parm *p; String *tm; - char source[256], target[256]; - - int use_self = (current == MEMBER_FUNC || current == MEMBER_VAR) ? 1 : 0; + String *source; + String *target; + + source = NewString(""); + target = NewString(""); + + int use_self = (current == MEMBER_FUNC || current == MEMBER_VAR || (current == CONSTRUCTOR_INITIALIZE && Swig_directorclass(n))) ? 1 : 0; int varargs = emit_isvarargs(l); Printf(kwargs,"{ "); @@ -622,13 +924,15 @@ public: String *ln = Getattr(p,"lname"); /* Produce string representation of source and target arguments */ + Clear(source); int selfp = (use_self && i == 0); if (selfp) - strcpy(source,"self"); + Printv(source,"self",NIL); else - sprintf(source,"argv[%d]",i-start); + Printf(source,"argv[%d]",i-start); - sprintf(target,"%s", Char(ln)); + Clear(target); + Printf(target,"%s",Char(ln)); if (i >= (numreq)) { /* Check if parsing an optional argument */ Printf(f->code," if (argc > %d) {\n", i - start); @@ -646,7 +950,7 @@ public: Replaceall(tm,"$target",ln); Replaceall(tm,"$source",source); Replaceall(tm,"$input",source); - Setattr(p,"emit:input",source); + Setattr(p,"emit:input",Copy(source)); Printf(f->code,"%s\n", tm); p = Getattr(p,"tmap:in:next"); } else { @@ -668,14 +972,18 @@ public: /* Trailing varargs */ if (varargs) { if (p && (tm = Getattr(p,"tmap:in"))) { - sprintf(source,"argv[%d]",i-start); + Clear(source); + Printf(source,"argv[%d]",i-start); Replaceall(tm,"$input",source); - Setattr(p,"emit:input",source); + Setattr(p,"emit:input",Copy(source)); Printf(f->code,"if (argc > %d) {\n", i-start); Printv(f->code,tm,"\n",NIL); Printf(f->code,"}\n"); } } + + Delete(source); + Delete(target); } /* --------------------------------------------------------------------- @@ -781,9 +1089,16 @@ public: * --------------------------------------------------------------------- */ virtual int functionWrapper(Node *n) { + String *nodeType; + bool constructor; + bool destructor; + String *storage; + bool isVirtual; + String *symname = Copy(Getattr(n,"sym:name")); SwigType *t = Getattr(n,"type"); ParmList *l = Getattr(n,"parms"); + Node *parent = Getattr(n,"parentNode"); String *tm; int need_result = 0; @@ -791,7 +1106,13 @@ public: /* Ruby needs no destructor wrapper */ if (current == DESTRUCTOR) return SWIG_NOWRAP; - + + nodeType = Getattr(n, "nodeType"); + constructor = (!Cmp(nodeType, "constructor")); + destructor = (!Cmp(nodeType, "destructor")); + storage = Getattr(n, "storage"); + isVirtual = (Cmp(storage, "virtual") == 0); + /* If the C++ class constructor is overloaded, we only want to * write out the "new" singleton method once since it is always * the same. (It's the "initialize" method that will handle the @@ -841,11 +1162,32 @@ public: int numreq = emit_num_required(l); int varargs = emit_isvarargs(l); bool allow_kwargs = use_kw || Getattr(n,"feature:kwargs"); - + int start = (current == MEMBER_FUNC || current == MEMBER_VAR) ? 1 : 0; + bool use_director = (current == CONSTRUCTOR_INITIALIZE && Swig_directorclass(n)); /* Now write the wrapper function itself */ - if (current != CONSTRUCTOR_ALLOCATE) { + if (current == CONSTRUCTOR_ALLOCATE) { + Printf(f->def, "#ifdef HAVE_RB_DEFINE_ALLOC_FUNC\n"); + Printv(f->def, "static VALUE\n", wname, "(VALUE self) {", NIL); + Printf(f->def, "#else\n"); + Printv(f->def, "static VALUE\n", wname, "(int argc, VALUE *argv, VALUE self) {", NIL); + Printf(f->def, "#endif\n"); + } else if (current == CONSTRUCTOR_INITIALIZE) { + int na = numarg; + int nr = numreq; + if (use_director) { + na--; + nr--; + } + Printv(f->def, "static VALUE\n", wname, "(int argc, VALUE *argv, VALUE self) {", NIL); + if (!varargs) { + Printf(f->code,"if ((argc < %d) || (argc > %d))\n", nr-start, na-start); + } else { + Printf(f->code,"if (argc < %d)\n", nr-start); + } + Printf(f->code,"rb_raise(rb_eArgError, \"wrong # of arguments(%%d for %d)\",argc);\n",nr-start); + } else { Printv(f->def, "static VALUE\n", wname, "(int argc, VALUE *argv, VALUE self) {", NIL); if (!varargs) { Printf(f->code,"if ((argc < %d) || (argc > %d))\n", numreq-start, numarg-start); @@ -853,18 +1195,18 @@ public: Printf(f->code,"if (argc < %d)\n", numreq-start); } Printf(f->code,"rb_raise(rb_eArgError, \"wrong # of arguments(%%d for %d)\",argc);\n",numreq-start); - } else { - Printf(f->def, "#ifdef HAVE_RB_DEFINE_ALLOC_FUNC\n"); - Printv(f->def, "static VALUE\n", wname, "(VALUE self) {", NIL); - Printf(f->def, "#else\n"); - Printv(f->def, "static VALUE\n", wname, "(int argc, VALUE *argv, VALUE self) {", NIL); - Printf(f->def, "#endif\n"); } /* Now walk the function parameter list and generate code */ /* to get arguments */ if (current != CONSTRUCTOR_ALLOCATE) { - marshalInputArgs(l, numarg, numreq, start, kwargs, allow_kwargs, f); + marshalInputArgs(n, l, numarg, numreq, start, kwargs, allow_kwargs, f); + } + + // FIXME? + if (use_director) { + numarg--; + numreq--; } /* Insert constraint checking code */ @@ -876,6 +1218,36 @@ public: /* Insert argument output code */ insertArgOutputCode(l, outarg, need_result); + /* if the object is a director, and the method call originated from its + * underlying python object, resolve the call by going up the c++ + * inheritance chain. otherwise try to resolve the method in python. + * without this check an infinite loop is set up between the director and + * shadow class method calls. + */ + + // NOTE: this code should only be inserted if this class is the + // base class of a director class. however, in general we haven't + // yet analyzed all classes derived from this one to see if they are + // directors. furthermore, this class may be used as the base of + // a director class defined in a completely different module at a + // later time, so this test must be included whether or not directorbase + // is true. we do skip this code if directors have not been enabled + // at the command line to preserve source-level compatibility with + // non-polymorphic swig. also, if this wrapper is for a smart-pointer + // method, there is no need to perform the test since the calling object + // (the smart-pointer) and the director object (the "pointee") are + // distinct. + + if (directorsEnabled()) { + if (!is_smart_pointer()) { + if (/*directorbase &&*/ !constructor && !destructor && isVirtual) { + Wrapper_add_local(f, "director", "__DIRECTOR__ *director = 0"); + Printf(f->code, "director = dynamic_cast<__DIRECTOR__*>(arg1);\n"); + Printf(f->code, "if (director && (director->__get_self() == self)) director->__set_up();\n"); + } + } + } + /* Now write code to make the function call */ if (current != CONSTRUCTOR_ALLOCATE) { if (current == CONSTRUCTOR_INITIALIZE) { @@ -887,9 +1259,6 @@ public: emit_action(n,f); } - int newobj = 0; - if (Getattr(n,"feature:new")) newobj = 1; - /* Return value if necessary */ if (SwigType_type(t) != T_VOID && current != CONSTRUCTOR_ALLOCATE && current != CONSTRUCTOR_INITIALIZE) { need_result = 1; @@ -901,8 +1270,40 @@ public: Replaceall(tm,"$result","vresult"); Replaceall(tm,"$source","result"); Replaceall(tm,"$target","vresult"); - Replaceall(tm,"$owner", newobj ? "1" : "0"); - Printv(f->code, tm, "\n", NIL); + if (Getattr(n, "feature:new")) + Replaceall(tm,"$owner", "1"); + else + Replaceall(tm,"$owner", "0"); + + // FIXME: this will not try to unwrap directors returned as non-director + // base class pointers! + + /* New addition to unwrap director return values so that the original + * python object is returned instead. + */ + bool unwrap = false; + String *decl = Getattr(n, "decl"); + int is_pointer = SwigType_ispointer_return(decl); + int is_reference = SwigType_isreference_return(decl); + if (is_pointer || is_reference) { + String *type = Getattr(n, "type"); + //Node *classNode = Swig_methodclass(n); + //Node *module = Getattr(classNode, "module"); + Node *module = Getattr(parent, "module"); + Node *target = Swig_directormap(module, type); + if (target) unwrap = true; + } + if (unwrap) { + Wrapper_add_local(f, "resultdirector", "__DIRECTOR__ *resultdirector = 0"); + Printf(f->code, "resultdirector = dynamic_cast<__DIRECTOR__*>(result);\n"); + Printf(f->code, "if (resultdirector) {\n"); + Printf(f->code, " resultobj = resultdirector->__get_self();\n"); + Printf(f->code, "} else {\n"); + Printf(f->code,"%s\n", tm); + Printf(f->code, "}\n"); + } else { + Printf(f->code,"%s\n", tm); + } } else { Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s.\n", SwigType_str(t,0)); @@ -930,7 +1331,7 @@ public: Printv(f->code,cleanup,NIL); /* Look for any remaining cleanup. This processes the %new directive */ - if (newobj) { + if (Getattr(n, "feature:new")) { tm = Swig_typemap_lookup_new("newfree",n,"result",0); if (tm) { Replaceall(tm,"$source","result"); @@ -1482,16 +1883,43 @@ public: * -------------------------------------------------------------------- */ virtual int constructorHandler(Node *n) { - /* First wrap the new singleton method */ + int use_director = Swig_directorclass(n); + + /* First wrap the allocate method */ current = CONSTRUCTOR_ALLOCATE; Swig_name_register((String_or_char *) "construct", (String_or_char *) "%c_allocate"); Language::constructorHandler(n); + /* + * If we're wrapping the constructor of a C++ director class, prepend a new parameter + * to receive the scripting language object (e.g. 'self') + * + */ + Swig_save("python:constructorHandler",n,"parms",NIL); + if (use_director) { + Parm *parms = Getattr(n, "parms"); + Parm *self; + String *name = NewString("self"); + String *type = NewString("VALUE"); + self = NewParm(type, name); + Delete(type); + Delete(name); + Setattr(self, "lname", "Qnil"); + if (parms) set_nextSibling(self, parms); + Setattr(n, "parms", self); + Setattr(n, "wrap:self", "1"); + Delete(self); + } + /* Now do the instance initialize method */ current = CONSTRUCTOR_INITIALIZE; Swig_name_register((String_or_char *) "construct", (String_or_char *) "new_%c"); Language::constructorHandler(n); + /* Restore original parameter list */ + Delattr(n, "wrap:self"); + Swig_restore(n); + /* Done */ Swig_name_unregister((String_or_char *) "construct"); current = NO_CPP; @@ -1552,8 +1980,7 @@ public: * This creates a pair of functions to set/get the variable of a member. * -------------------------------------------------------------------- */ - virtual int - membervariableHandler(Node *n) { + virtual int membervariableHandler(Node *n) { current = MEMBER_VAR; Language::membervariableHandler(n); current = NO_CPP; @@ -1566,8 +1993,7 @@ public: * Wrap a static C++ function * ---------------------------------------------------------------------- */ - virtual int - staticmemberfunctionHandler(Node *n) { + virtual int staticmemberfunctionHandler(Node *n) { current = STATIC_FUNC; Language::staticmemberfunctionHandler(n); current = NO_CPP; @@ -1580,8 +2006,7 @@ public: * Create a C++ constant * --------------------------------------------------------------------- */ - virtual int - memberconstantHandler(Node *n) { + virtual int memberconstantHandler(Node *n) { current = CLASS_CONST; Language::memberconstantHandler(n); current = NO_CPP; @@ -1592,14 +2017,464 @@ public: * staticmembervariableHandler() * --------------------------------------------------------------------- */ - virtual int - staticmembervariableHandler(Node *n) { + virtual int staticmembervariableHandler(Node *n) { current = STATIC_VAR; Language::staticmembervariableHandler(n); current = NO_CPP; return SWIG_OK; } + /* C++ director class generation */ + virtual int classDirector(Node *n) { + return Language::classDirector(n); + } + + virtual int classDirectorInit(Node *n) { + String *declaration; + declaration = Swig_director_declaration(n); + Printf(f_directors_h, "\n"); + Printf(f_directors_h, "%s\n", declaration); + Printf(f_directors_h, "public:\n"); + Delete(declaration); + return Language::classDirectorInit(n); + } + + virtual int classDirectorEnd(Node *n) { + Printf(f_directors_h, "};\n\n"); + return Language::classDirectorEnd(n); + } + + virtual int unrollVirtualMethods(Node *n, Node *parent, Hash *vm, int default_director, int &virtual_destructor) { + return Language::unrollVirtualMethods(n, parent, vm, default_director, virtual_destructor); + } + + /* ------------------------------------------------------------ + * classDirectorConstructor() + * ------------------------------------------------------------ */ + + virtual int classDirectorConstructor(Node *n) { + Node *parent = Getattr(n, "parentNode"); + String *sub = NewString(""); + String *decl = Getattr(n, "decl"); + String *supername = Swig_class_name(parent); + String *classname = NewString(""); + Printf(classname, "__DIRECTOR__%s", supername); + + /* insert self and __disown parameters */ + Parm *p, *ip; + ParmList *superparms = Getattr(n, "parms"); + ParmList *parms = CopyParmList(superparms); + String *type = NewString("PyObject"); + SwigType_add_pointer(type); + p = NewParm(type, NewString("self")); + set_nextSibling(p, parms); + parms = p; + for (ip = parms; nextSibling(ip); ) ip = nextSibling(ip); + p = NewParm(NewString("int"), NewString("__disown")); + Setattr(p, "value", "1"); + set_nextSibling(ip, p); + + /* constructor */ + { + Wrapper *w = NewWrapper(); + String *call; + String *basetype = Getattr(parent, "classtype"); + String *target = method_decl(decl, classname, parms, 0, 0); + call = Swig_csuperclass_call(0, basetype, superparms); + Printf(w->def, "%s::%s: %s, __DIRECTOR__(self, __disown) { }", classname, target, call); + Delete(target); + Wrapper_print(w, f_directors); + Delete(call); + DelWrapper(w); + } + + /* constructor header */ + { + String *target = method_decl(decl, classname, parms, 0, 1); + Printf(f_directors_h, " %s;\n", target); + Delete(target); + } + + Delete(sub); + Delete(classname); + Delete(supername); + Delete(parms); + return Language::classDirectorConstructor(n); + } + + virtual int classDirectorDefaultConstructor(Node *n) { + String *classname; + Wrapper *w; + classname = Swig_class_name(n); + w = NewWrapper(); + Printf(w->def, "__DIRECTOR__%s::__DIRECTOR__%s(VALUE self, bool __disown) : __DIRECTOR__(self, __disown) { }", classname, classname); + Wrapper_print(w, f_directors); + DelWrapper(w); + Printf(f_directors_h, " __DIRECTOR__%s(VALUE self, bool __disown = true);\n", classname); + Delete(classname); + return Language::classDirectorDefaultConstructor(n); + } + + /* --------------------------------------------------------------- + * classDirectorMethod() + * + * Emit a virtual director method to pass a method call on to the + * underlying Python object. + * + * --------------------------------------------------------------- */ + + virtual int classDirectorMethod(Node *n, Node *parent, String *super) { + int is_void = 0; + int is_pointer = 0; + String *decl; + String *type; + String *name; + String *classname; + String *declaration; + ParmList *l; + Wrapper *w; + String *tm; + String *wrap_args; + String *return_type; + String *value = Getattr(n, "value"); + String *storage = Getattr(n,"storage"); + bool pure_virtual = false; + int status = SWIG_OK; + int idx; + + if (Cmp(storage,"virtual") == 0) { + if (Cmp(value,"0") == 0) { + pure_virtual = true; + } + } + + classname = Getattr(parent, "sym:name"); + type = Getattr(n, "type"); + name = Getattr(n, "name"); + + w = NewWrapper(); + declaration = NewString(""); + + /* determine if the method returns a pointer */ + decl = Getattr(n, "decl"); + is_pointer = SwigType_ispointer_return(decl); + is_void = (!Cmp(type, "void") && !is_pointer); + + /* form complete return type */ + return_type = Copy(type); + { + SwigType *t = Copy(decl); + SwigType *f = 0; + f = SwigType_pop_function(t); + SwigType_push(return_type, t); + Delete(f); + Delete(t); + } + + /* virtual method definition */ + l = Getattr(n, "parms"); + String *target; + String *pclassname = NewStringf("__DIRECTOR__%s", classname); + String *qualified_name = NewStringf("%s::%s", pclassname, name); + target = method_decl(decl, qualified_name, l, 0, 0); + String *rtype = SwigType_str(type, 0); + Printf(w->def, "%s %s {", rtype, target); + Delete(qualified_name); + Delete(target); + /* header declaration */ + target = method_decl(decl, name, l, 0, 1); + Printf(declaration, " virtual %s %s;\n", rtype, target); + Delete(target); + + /* attach typemaps to arguments (C/C++ -> Ruby) */ + String *arglist = NewString(""); + String* parse_args = NewString(""); + + Swig_typemap_attach_parms("in", l, w); + Swig_typemap_attach_parms("inv", l, w); + Swig_typemap_attach_parms("outv", l, w); + Swig_typemap_attach_parms("argoutv", l, w); + + Parm* p; + int num_arguments = emit_num_arguments(l); + int i; + char source[256]; + + wrap_args = NewString(""); + int outputs = 0; + if (!is_void) outputs++; + + /* build argument list and type conversion string */ + for (i=0, idx=0, p = l; i < num_arguments; i++) { + + while (Getattr(p, "tmap:ignore")) { + p = Getattr(p, "tmap:ignore:next"); + } + + if (Getattr(p, "tmap:argoutv") != 0) outputs++; + + String* pname = Getattr(p, "name"); + String* ptype = Getattr(p, "type"); + + Putc(',',arglist); + if ((tm = Getattr(p, "tmap:inv")) != 0) { + String* parse = Getattr(p, "tmap:inv:parse"); + if (!parse) { + sprintf(source, "obj%d", idx++); + Replaceall(tm, "$input", source); + Replaceall(tm, "$owner", "0"); + Printv(wrap_args, tm, "\n", NIL); + Wrapper_add_localv(w, source, "VALUE", source, "= 0", NIL); + Printv(arglist, source, NIL); + Putc('O', parse_args); + } else { + Printf(parse_args, "%s", parse); + Replaceall(tm, "$input", pname); + Replaceall(tm, "$owner", "0"); + if (Len(tm) == 0) Append(tm, pname); + Printf(arglist, "%s", tm); + } + p = Getattr(p, "tmap:inv:next"); + continue; + } else + if (Cmp(ptype, "void")) { + /** + * Special handling for pointers to other C++ director classes. + * Ideally this would be left to a typemap, but there is currently no + * way to selectively apply the dynamic_cast<> to classes that have + * directors. In other words, the type "__DIRECTOR__$1_lname" only exists + * for classes with directors. We avoid the problem here by checking + * module.wrap::directormap, but it's not clear how to get a typemap to + * do something similar. Perhaps a new default typemap (in addition + * to SWIGTYPE) called DIRECTORTYPE? + */ + if (SwigType_ispointer(ptype) || SwigType_isreference(ptype)) { + Node *module = Getattr(parent, "module"); + Node *target = Swig_directormap(module, ptype); + sprintf(source, "obj%d", idx++); + String *nonconst = 0; + /* strip pointer/reference --- should move to Swig/stype.c */ + String *nptype = NewString(Char(ptype)+2); + /* name as pointer */ + String *ppname = Copy(pname); + if (SwigType_isreference(ptype)) { + Insert(ppname,0,"&"); + } + /* if necessary, cast away const since Ruby doesn't support it! */ + if (SwigType_isconst(nptype)) { + nonconst = NewStringf("nc_tmp_%s", pname); + String *nonconst_i = NewStringf("= const_cast<%s>(%s)", SwigType_lstr(ptype, 0), ppname); + Wrapper_add_localv(w, nonconst, SwigType_lstr(ptype, 0), nonconst, nonconst_i, NIL); + Delete(nonconst_i); + Swig_warning(WARN_LANG_DISCARD_CONST, input_file, line_number, + "Target language argument '%s' discards const in director method %s::%s.\n", SwigType_str(ptype, pname), classname, name); + } else { + nonconst = Copy(ppname); + } + Delete(nptype); + Delete(ppname); + String *mangle = SwigType_manglestr(ptype); + if (target) { + String *director = NewStringf("director_%s", mangle); + Wrapper_add_localv(w, director, "__DIRECTOR__ *", director, "= 0", NIL); + Wrapper_add_localv(w, source, "VALUE", source, "= Qnil", NIL); + Printf(wrap_args, "%s = dynamic_cast<__DIRECTOR__*>(%s);\n", director, nonconst); + Printf(wrap_args, "if (!%s) {\n", director); + Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE%s, 0);\n", source, nonconst, mangle); + Printf(wrap_args, "} else {\n"); + Printf(wrap_args, "%s = %s->__get_self();\n", source, director); + Printf(wrap_args, "}\n"); + Printf(wrap_args, "assert(%s != Qnil);\n", source); + Delete(director); + Printv(arglist, source, NIL); + } else { + Wrapper_add_localv(w, source, "VALUE", source, "= Qnil", NIL); + Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE%s, 0);\n", + source, nonconst, mangle); + //Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE_p_%s, 0);\n", + // source, nonconst, base); + Printv(arglist, source, NIL); + } + Putc('O', parse_args); + Delete(mangle); + Delete(nonconst); + } else { + Swig_warning(WARN_TYPEMAP_INV_UNDEF, input_file, line_number, + "Unable to use type %s as a function argument in director method %s::%s (skipping method).\n", SwigType_str(ptype, 0), classname, name); + status = SWIG_NOWRAP; + break; + } + } + p = nextSibling(p); + } + + /* declare method return value + * if the return value is a reference or const reference, a specialized typemap must + * handle it, including declaration of c_result ($result). + */ + if (!is_void) { + Wrapper_add_localv(w, "c_result", SwigType_lstr(return_type, "c_result"), NIL); + } + /* declare Ruby return value */ + Wrapper_add_local(w, "result", "VALUE result"); + + /* direct call to superclass if _up is set */ + Printf(w->code, "if (__get_up()) {\n"); + if (pure_virtual) { + Printf(w->code, "throw SWIG_DIRECTOR_PURE_VIRTUAL_EXCEPTION();\n"); + } else { + if (is_void) { + Printf(w->code, "%s;\n", Swig_method_call(super,l)); + Printf(w->code, "return;\n"); + } else { + Printf(w->code, "return %s;\n", Swig_method_call(super,l)); + } + } + Printf(w->code, "}\n"); + + /* check that have a wrapped Ruby object */ + Printv(w->code, "assert(__get_self() != Qnil);\n", NIL); + + /* wrap complex arguments to PyObjects */ + Printv(w->code, wrap_args, NIL); + + String *pyname = Getattr(n,"sym:name"); + + /* pass the method call on to the Ruby object */ + if (Len(parse_args) > 0) { + Printf(w->code, "result = rb_funcall(__get_self(), rb_intern(\"%s\"), %d%s);\n", pyname, 0, arglist); + } else { + Printf(w->code, "result = rb_funcall(__get_self(), rb_intern(\"%s\"), 0, NULL);\n", pyname); + } + + /* exception handling */ + tm = Swig_typemap_lookup_new("director:except", n, "result", 0); + if (!tm) { + tm = Getattr(n, "feature:director:except"); + } + if ((tm) && Len(tm) && (Strcmp(tm, "1") != 0)) { + Printf(w->code, "if (result == NULL) {\n"); + Printf(w->code, " PyObject *error = PyErr_Occurred();\n"); + Replaceall(tm, "$error", "error"); + Printv(w->code, Str(tm), "\n", NIL); + Printf(w->code, "}\n"); + } + + /* + * Ruby method may return a simple object, or an Array of objects. + * For in/out arguments, we have to extract the appropriate VALUEs from the Array, + * then marshal everything back to C/C++ (return value and output arguments). + */ + + /* Marshal return value and other outputs (if any) from VALUE to C/C++ type */ + + String* cleanup = NewString(""); + String* outarg = NewString(""); + + if (outputs > 1) { + Wrapper_add_local(w, "output", "VALUE output"); + Printf(w->code, "if (TYPE(result) != T_ARRAY) {\n"); + Printf(w->code, "throw SWIG_DIRECTOR_TYPE_MISMATCH(\"Ruby method failed to return an array.\");\n"); + Printf(w->code, "}\n"); + } + + idx = 0; + + /* Marshal return value */ + if (!is_void) { + /* This seems really silly. The node's type excludes qualifier/pointer/reference markers, + * which have to be retrieved from the decl field to construct return_type. But the typemap + * lookup routine uses the node's type, so we have to swap in and out the correct type. + * It's not just me, similar silliness also occurs in Language::cDeclaration(). + */ + Setattr(n, "type", return_type); + tm = Swig_typemap_lookup_new("outv", n, "result", w); + Setattr(n, "type", type); + if (tm == 0) { + String *name = NewString("result"); + tm = Swig_typemap_search("outv", return_type, name, NULL); + Delete(name); + } + if (tm != 0) { + if (outputs > 1) { + Printf(w->code, "output = rb_ary_entry(result, %d);\n", idx++); + Replaceall(tm, "$input", "output"); + } else { + Replaceall(tm, "$input", "result"); + } + /* TODO check this */ + if (Getattr(n,"wrap:disown")) { + Replaceall(tm,"$disown","SWIG_POINTER_DISOWN"); + } else { + Replaceall(tm,"$disown","0"); + } + Replaceall(tm, "$result", "c_result"); + Printv(w->code, tm, "\n", NIL); + } else { + Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, + "Unable to return type %s in director method %s::%s (skipping method).\n", SwigType_str(return_type, 0), classname, name); + status = SWIG_ERROR; + } + } + + /* Marshal outputs */ + for (p = l; p; ) { + if ((tm = Getattr(p, "tmap:argoutv")) != 0) { + if (outputs > 1) { + Printf(w->code, "output = rb_ary_entry(result, %d);\n", idx++); + Replaceall(tm, "$input", "output"); + } else { + Replaceall(tm, "$input", "result"); + } + Replaceall(tm, "$result", Getattr(p, "name")); + Printv(w->code, tm, "\n", NIL); + p = Getattr(p, "tmap:argoutv:next"); + } else { + p = nextSibling(p); + } + } + + /* any existing helper functions to handle this? */ + if (!is_void) { + if (!SwigType_isreference(return_type)) { + Printf(w->code, "return c_result;\n"); + } else { + Printf(w->code, "return *c_result;\n"); + } + } + + Printf(w->code, "}\n"); + + /* emit the director method */ + if (status == SWIG_OK) { + Wrapper_print(w, f_directors); + Printv(f_directors_h, declaration, NIL); + } + + /* clean up */ + Delete(wrap_args); + Delete(parse_args); + Delete(arglist); + Delete(rtype); + Delete(return_type); + Delete(pclassname); + Delete(cleanup); + Delete(outarg); + DelWrapper(w); + return status; + } + + virtual int classDirectorConstructors(Node *n) { + return Language::classDirectorConstructors(n); + } + + virtual int classDirectorMethods(Node *n) { + return Language::classDirectorMethods(n); + } + + virtual int classDirectorDisown(Node *n) { + return Language::classDirectorDisown(n); + } }; /* class RUBY */ /* -----------------------------------------------------------------------------