diff options
author | robot-piglet <[email protected]> | 2025-08-28 14:27:58 +0300 |
---|---|---|
committer | robot-piglet <[email protected]> | 2025-08-28 14:57:06 +0300 |
commit | 81d828c32c8d5477cb2f0ce5da06a1a8d9392ca3 (patch) | |
tree | 3081d566f0d5158d76e9093261344f6406fd09f7 /contrib/tools/swig/Source/Modules/ruby.cxx | |
parent | 77ea11423f959e51795cc3ef36a48d808b4ffb98 (diff) |
Intermediate changes
commit_hash:d5b1af16dbe9030537a04c27eb410c88c2f496cd
Diffstat (limited to 'contrib/tools/swig/Source/Modules/ruby.cxx')
-rw-r--r-- | contrib/tools/swig/Source/Modules/ruby.cxx | 3468 |
1 files changed, 3468 insertions, 0 deletions
diff --git a/contrib/tools/swig/Source/Modules/ruby.cxx b/contrib/tools/swig/Source/Modules/ruby.cxx new file mode 100644 index 00000000000..44f2596ddb7 --- /dev/null +++ b/contrib/tools/swig/Source/Modules/ruby.cxx @@ -0,0 +1,3468 @@ +/* ----------------------------------------------------------------------------- + * This file is part of SWIG, which is licensed as a whole under version 3 + * (or any later version) of the GNU General Public License. Some additional + * terms also apply to certain portions of SWIG. The full details of the SWIG + * license and copyrights can be found in the LICENSE and COPYRIGHT files + * included with the SWIG source code as distributed by the SWIG developers + * and at https://www.swig.org/legal.html. + * + * ruby.cxx + * + * Ruby language module for SWIG. + * ----------------------------------------------------------------------------- */ + +#include "swigmod.h" +#include "cparse.h" +#include <ctype.h> +#include <string.h> +#include <limits.h> /* for INT_MAX */ + +#define SWIG_PROTECTED_TARGET_METHODS 1 + +class RClass { +private: + String *temp; + +public: + String *name; /* class name (renamed) */ + String *cname; /* original C class/struct name */ + String *mname; /* Mangled name */ + + /** + * The C variable name used in the SWIG-generated wrapper code to refer to + * this class; usually it is of the form "SwigClassXXX.klass", where SwigClassXXX + * is a swig_class struct instance and klass is a member of that struct. + */ + String *vname; + + /** + * The C variable name used in the SWIG-generated wrapper code to refer to + * the module that implements this class's methods (when we're trying to + * support C++ multiple inheritance). Usually it is of the form + * "SwigClassClassName.mImpl", where SwigClassXXX is a swig_class struct instance + * and mImpl is a member of that struct. + */ + String *mImpl; + + String *type; + String *prefix; + String *init; + + + int constructor_defined; + int destructor_defined; + + RClass() { + temp = NewString(""); + name = NewString(""); + cname = NewString(""); + mname = NewString(""); + vname = NewString(""); + mImpl = NewString(""); + type = NewString(""); + prefix = NewString(""); + init = NewString(""); + constructor_defined = 0; + destructor_defined = 0; + } + + ~RClass() { + Delete(name); + Delete(cname); + Delete(vname); + Delete(mImpl); + Delete(mname); + Delete(type); + Delete(prefix); + Delete(init); + Delete(temp); + } + + void set_name(const_String_or_char_ptr cn, const_String_or_char_ptr rn, const_String_or_char_ptr valn) { + /* Original C/C++ class (or struct) name */ + Clear(cname); + Append(cname, cn); + + /* Mangled name */ + Delete(mname); + mname = Swig_name_mangle_string(cname); + + /* Renamed class name */ + Clear(name); + Append(name, valn); + + /* Variable name for the VALUE that refers to the Ruby Class object */ + Clear(vname); + Printf(vname, "SwigClass%s.klass", name); + + /* Variable name for the VALUE that refers to the Ruby Class object */ + Clear(mImpl); + Printf(mImpl, "SwigClass%s.mImpl", name); + + /* Prefix */ + Clear(prefix); + Printv(prefix, (rn ? rn : cn), "_", NIL); + } + + char *strip(const_String_or_char_ptr s) { + Clear(temp); + if (Strncmp(s, prefix, Len(prefix)) == 0) { + Append(temp, Char(s) + Len(prefix)); + } else { + Append(temp, s); + } + return Char(temp); + } +}; + + +/* flags for the make_autodoc function */ +namespace { +enum autodoc_t { + AUTODOC_CLASS, + AUTODOC_CTOR, + AUTODOC_DTOR, + AUTODOC_STATICFUNC, + AUTODOC_FUNC, + AUTODOC_METHOD, + AUTODOC_GETTER, + AUTODOC_SETTER, + AUTODOC_NONE +}; +} + +static const char *usage = "\ +Ruby Options (available with -ruby)\n\ + -autorename - Enable renaming of classes and methods to follow Ruby coding standards\n\ + -globalmodule - Wrap everything into the global module\n\ + -initname <name>- Set entry function to Init_<name> (used by `require')\n\ + -minherit - Attempt to support multiple inheritance\n\ + -noautorename - Disable renaming of classes and methods (default)\n\ + -prefix <name> - Set a prefix <name> to be prepended to all names\n\ +"; + + +#define RCLASS(hash, name) (RClass*)(Getattr(hash, name) ? Data(Getattr(hash, name)) : 0) +#define SET_RCLASS(hash, name, klass) Setattr(hash, name, NewVoid(klass, 0)) + + +class RUBY:public Language { +private: + + String *module; + String *modvar; + String *feature; + String *prefix; + int current; + 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_directors_helpers; + File *f_begin; + File *f_runtime; + File *f_runtime_h; + File *f_header; + File *f_wrappers; + File *f_init; + File *f_initbeforefunc; + + bool useGlobalModule; + bool multipleInheritance; + + // Wrap modes + enum WrapperMode { + NO_CPP, + MEMBER_FUNC, + CONSTRUCTOR_ALLOCATE, + CONSTRUCTOR_INITIALIZE, + DESTRUCTOR, + MEMBER_VAR, + CLASS_CONST, + STATIC_FUNC, + STATIC_VAR + }; + + /* ------------------------------------------------------------ + * autodoc level declarations + * ------------------------------------------------------------ */ + + enum autodoc_l { + NO_AUTODOC = -2, // no autodoc + STRING_AUTODOC = -1, // use provided string + NAMES_AUTODOC = 0, // only parameter names + TYPES_AUTODOC = 1, // parameter names and types + EXTEND_AUTODOC = 2, // extended documentation and parameter names + EXTEND_TYPES_AUTODOC = 3 // extended documentation and parameter types + names + }; + + autodoc_t last_mode; + String* last_autodoc; + + autodoc_l autodoc_level(String *autodoc) { + autodoc_l dlevel = NO_AUTODOC; + char *c = Char(autodoc); + if (c) { + if (isdigit(c[0])) { + dlevel = (autodoc_l) atoi(c); + } else { + if (strcmp(c, "extended") == 0) { + dlevel = EXTEND_AUTODOC; + } else { + dlevel = STRING_AUTODOC; + } + } + } + return dlevel; + } + + + + /* ------------------------------------------------------------ + * have_docstring() + * Check if there is a docstring directive and it has text, + * or there is an autodoc flag set + * ------------------------------------------------------------ */ + + bool have_docstring(Node *n) { + String *str = Getattr(n, "feature:docstring"); + return (str && Len(str) > 0) || (Getattr(n, "feature:autodoc") && !GetFlag(n, "feature:noautodoc")); + } + + /* ------------------------------------------------------------ + * docstring() + * Get the docstring text, stripping off {} if necessary, + * and enclose in triple double quotes. If autodoc is also + * set then it will build a combined docstring. + * ------------------------------------------------------------ */ + + String *docstring(Node *n, autodoc_t ad_type) { + + String *str = Getattr(n, "feature:docstring"); + bool have_ds = (str && Len(str) > 0); + bool have_auto = (Getattr(n, "feature:autodoc") && !GetFlag(n, "feature:noautodoc")); + String *autodoc = NULL; + String *doc = NULL; + + if (have_ds) { + char *t = Char(str); + if (*t == '{') { + Delitem(str, 0); + Delitem(str, DOH_END); + } + } + + if (have_auto) { + autodoc = make_autodoc(n, ad_type); + have_auto = (autodoc && Len(autodoc) > 0); + } + + if (have_auto || have_ds) + doc = NewString("/*"); + + if (have_auto && have_ds) { // Both autodoc and docstring are present + Printv(doc, "\n", autodoc, "\n", str, "\n", NIL); + } else if (!have_auto && have_ds) { // only docstring + Printv(doc, str, NIL); + } else if (have_auto && !have_ds) { // only autodoc + Printv(doc, "\n", autodoc, "\n", NIL); + } else { + doc = NewString(""); + } + + if (have_auto || have_ds) + Append(doc, "*/\n"); + + // Save the generated strings in the parse tree in case they are used later + // by post processing tools + Setattr(n, "ruby:docstring", doc); + Setattr(n, "ruby:autodoc", autodoc); + return doc; + } + + /* ----------------------------------------------------------------------------- + * addMissingParameterNames() + * For functions that have not had nameless parameters set in the Language class. + * + * Inputs: + * plist - entire parameter list + * arg_offset - argument number for first parameter + * Side effects: + * The "lname" attribute in each parameter in plist will be contain a parameter name + * ----------------------------------------------------------------------------- */ + + void addMissingParameterNames(Node* n, ParmList *plist, int arg_offset) { + Parm *p = plist; + int i = arg_offset; + while (p) { + if (!Getattr(p, "lname")) { + String *name = makeParameterName(n, p, i); + Setattr(p, "lname", name); + Delete(name); + } + i++; + p = nextSibling(p); + } + } + + /* ------------------------------------------------------------ + * make_autodocParmList() + * Generate the documentation for the function parameters + * ------------------------------------------------------------ */ + + String *make_autodocParmList(Node *n, bool showTypes) { + String *doc = NewString(""); + String *pdocs = 0; + ParmList *plist = CopyParmList(Getattr(n, "parms")); + Parm *p; + Parm *pnext; + int lines = 0; + int arg_num = is_wrapping_class() ? 1 : 0; + const int maxwidth = 80; + + addMissingParameterNames(n, plist, arg_num); // for $1_name substitutions done in Swig_typemap_attach_parms + + Swig_typemap_attach_parms("in", plist, 0); + Swig_typemap_attach_parms("doc", plist, 0); + + if (Strcmp(ParmList_protostr(plist), "void") == 0) { + //No parameters actually + return doc; + } + + for (p = plist; p; p = pnext, arg_num++) { + + String *tm = Getattr(p, "tmap:in"); + if (tm) { + pnext = Getattr(p, "tmap:in:next"); + if (checkAttribute(p, "tmap:in:numinputs", "0")) { + continue; + } + } else { + pnext = nextSibling(p); + } + + String *name = 0; + String *type = 0; + String *value = 0; + String *pdoc = Getattr(p, "tmap:doc"); + if (pdoc) { + name = Getattr(p, "tmap:doc:name"); + type = Getattr(p, "tmap:doc:type"); + value = Getattr(p, "tmap:doc:value"); + } + + // Note: the generated name should be consistent with that in kwnames[] + String *made_name = 0; + if (!name) { + name = made_name = makeParameterName(n, p, arg_num); + } + + type = type ? type : Getattr(p, "type"); + value = value ? value : Getattr(p, "value"); + + if (SwigType_isvarargs(type)) + break; + + // Skip the 'self' parameter which in ruby is implicit + if ( Cmp(name, "self") == 0 ) + continue; + + // Make __p parameters just p (as used in STL) + Replace( name, "__", "", DOH_REPLACE_FIRST ); + + if (Len(doc)) { + // add a comma to the previous one if any + Append(doc, ", "); + + // Do we need to wrap a long line? + if ((Len(doc) - lines * maxwidth) > maxwidth) { + Printf(doc, "\n%s", tab4); + lines += 1; + } + } + + // Do the param type too? + Node *nn = classLookup(Getattr(p, "type")); + String *type_str = nn ? Copy(Getattr(nn, "sym:name")) : SwigType_str(type, 0); + if (showTypes) + Printf(doc, "%s ", type_str); + + Append(doc, name); + if (pdoc) { + if (!pdocs) + pdocs = NewString("Parameters:\n"); + Printf(pdocs, " %s.\n", pdoc); + } + + if (value) { + String *new_value = convertValue(value, Getattr(p, "numval"), Getattr(p, "stringval"), Getattr(p, "type")); + if (new_value) { + value = new_value; + } else { + Node *lookup = Swig_symbol_clookup(value, 0); + if (lookup) + value = Getattr(lookup, "sym:name"); + } + Printf(doc, "=%s", value); + + if (new_value) + Delete(new_value); + } + Delete(type_str); + Delete(made_name); + } + if (pdocs) + Setattr(n, "feature:pdocs", pdocs); + Delete(plist); + return doc; + } + + /* ------------------------------------------------------------ + * make_autodoc() + * Build a docstring for the node, using parameter and other + * info in the parse tree. If the value of the autodoc + * attribute is "0" then do not include parameter types, if + * it is "1" (the default) then do. If it has some other + * value then assume it is supplied by the extension writer + * and use it directly. + * ------------------------------------------------------------ */ + + String *make_autodoc(Node *n, autodoc_t ad_type) { + int extended = 0; + // If the function is overloaded then this function is called + // for the last one. Rewind to the first so the docstrings are + // in order. + while (Getattr(n, "sym:previousSibling")) + n = Getattr(n, "sym:previousSibling"); + + Node *pn = Swig_methodclass(n); + String* super_names = NewString(""); + String* class_name = Getattr(pn, "sym:name") ; + + if ( !class_name ) { + class_name = NewString(""); + } else { + class_name = Copy(class_name); + List *baselist = Getattr(pn, "bases"); + if (baselist && Len(baselist)) { + Iterator base = First(baselist); + while (base.item && GetFlag(base.item, "feature:ignore")) { + base = Next(base); + } + + int count = 0; + for ( ;base.item; ++count) { + if ( count ) Append(super_names, ", "); + String *basename = Getattr(base.item, "sym:name"); + + String* basenamestr = NewString(basename); + Node* parent = parentNode(base.item); + while (parent) + { + String *parent_name = Copy( Getattr(parent, "sym:name") ); + if ( !parent_name ) { + Node* mod = Getattr(parent, "module"); + if ( mod ) + parent_name = Copy( Getattr(mod, "name") ); + if ( parent_name ) + (Char(parent_name))[0] = (char)toupper((Char(parent_name))[0]); + } + if ( parent_name ) { + Insert(basenamestr, 0, "::"); + Insert(basenamestr, 0, parent_name); + Delete(parent_name); + } + parent = parentNode(parent); + } + + Append(super_names, basenamestr ); + Delete(basenamestr); + base = Next(base); + } + } + } + String* full_name; + if ( module ) { + full_name = NewString(module); + if (Len(class_name) > 0) + Append(full_name, "::"); + } + else + full_name = NewString(""); + Append(full_name, class_name); + + String* symname = Getattr(n, "sym:name"); + if ( Getattr( special_methods, symname ) ) + symname = Getattr( special_methods, symname ); + + String* methodName = NewString(full_name); + Append(methodName, symname); + + + // Each overloaded function will try to get documented, + // so we keep the name of the last overloaded function and its type. + // Documenting just from functionWrapper() is not possible as + // sym:name has already been changed to include the class name + if ( last_mode == ad_type && Cmp(methodName, last_autodoc) == 0 ) { + Delete(full_name); + Delete(class_name); + Delete(super_names); + Delete(methodName); + return NewString(""); + } + + + last_mode = ad_type; + last_autodoc = Copy(methodName); + + String *doc = NewString(""); + int counter = 0; + bool skipAuto = false; + Node* on = n; + for ( ; n; ++counter ) { + String *type_str = NULL; + skipAuto = false; + bool showTypes = false; + String *autodoc = Getattr(n, "feature:autodoc"); + autodoc_l dlevel = autodoc_level(autodoc); + switch (dlevel) { + case NO_AUTODOC: + break; + case NAMES_AUTODOC: + showTypes = false; + break; + case TYPES_AUTODOC: + showTypes = true; + break; + case EXTEND_AUTODOC: + extended = 1; + showTypes = false; + break; + case EXTEND_TYPES_AUTODOC: + extended = 1; + showTypes = true; + break; + case STRING_AUTODOC: + skipAuto = true; + break; + } + + SwigType *type = Getattr(n, "type"); + + if (type) { + if (Strcmp(type, "void") == 0) { + type_str = NULL; + } else { + SwigType *qt = SwigType_typedef_resolve_all(type); + if (SwigType_isenum(qt)) { + type_str = NewString("int"); + } else { + Node *nn = classLookup(type); + type_str = nn ? Copy(Getattr(nn, "sym:name")) : SwigType_str(type, 0); + } + } + } + + if (counter == 0) { + switch (ad_type) { + case AUTODOC_CLASS: + Printf(doc, " Document-class: %s", full_name); + if ( Len(super_names) > 0 ) + Printf( doc, " < %s", super_names); + Append(doc, "\n\n"); + break; + case AUTODOC_CTOR: + Printf(doc, " Document-method: %s.new\n\n", full_name); + break; + + case AUTODOC_DTOR: + break; + + case AUTODOC_STATICFUNC: + Printf(doc, " Document-method: %s.%s\n\n", full_name, symname); + break; + + case AUTODOC_FUNC: + case AUTODOC_METHOD: + case AUTODOC_GETTER: + Printf(doc, " Document-method: %s.%s\n\n", full_name, symname); + break; + case AUTODOC_SETTER: + Printf(doc, " Document-method: %s.%s=\n\n", full_name, symname); + break; + case AUTODOC_NONE: + break; + } + } + + if (skipAuto) { + if ( counter == 0 ) Printf(doc, " call-seq:\n"); + switch( ad_type ) + { + case AUTODOC_STATICFUNC: + case AUTODOC_FUNC: + case AUTODOC_METHOD: + case AUTODOC_GETTER: + { + String *paramList = make_autodocParmList(n, showTypes); + if (Len(paramList)) + Printf(doc, " %s(%s)", symname, paramList); + else + Printf(doc, " %s", symname); + if (type_str) + Printf(doc, " -> %s", type_str); + break; + } + case AUTODOC_SETTER: + { + Printf(doc, " %s=(x)", symname); + if (type_str) + Printf(doc, " -> %s", type_str); + break; + } + default: + break; + } + } else { + switch (ad_type) { + case AUTODOC_CLASS: + { + // Only do the autodoc if there isn't a docstring for the class + String *str = Getattr(n, "feature:docstring"); + if (counter == 0 && (str == 0 || Len(str) == 0)) { + if (CPlusPlus) { + Printf(doc, " Proxy of C++ %s class", full_name); + } else { + Printf(doc, " Proxy of C %s struct", full_name); + } + } + } + break; + case AUTODOC_CTOR: + if (counter == 0) + Printf(doc, " call-seq:\n"); + if (Strcmp(class_name, symname) == 0) { + String *paramList = make_autodocParmList(n, showTypes); + if (Len(paramList)) + Printf(doc, " %s.new(%s)", class_name, paramList); + else + Printf(doc, " %s.new", class_name); + } else { + Printf(doc, " %s.new(%s)", class_name, make_autodocParmList(n, showTypes)); + } + break; + + case AUTODOC_DTOR: + break; + + case AUTODOC_STATICFUNC: + case AUTODOC_FUNC: + case AUTODOC_METHOD: + case AUTODOC_GETTER: + { + if (counter == 0) + Printf(doc, " call-seq:\n"); + String *paramList = make_autodocParmList(n, showTypes); + if (Len(paramList)) + Printf(doc, " %s(%s)", symname, paramList); + else + Printf(doc, " %s", symname); + if (type_str) + Printf(doc, " -> %s", type_str); + break; + } + case AUTODOC_SETTER: + { + Printf(doc, " call-seq:\n"); + Printf(doc, " %s=(x)", symname); + if (type_str) + Printf(doc, " -> %s", type_str); + break; + } + case AUTODOC_NONE: + break; + } + } + + // if it's overloaded then get the next decl and loop around again + n = Getattr(n, "sym:nextSibling"); + if (n) + Append(doc, "\n"); + Delete(type_str); + } + + Printf(doc, "\n\n"); + if (!skipAuto) { + switch (ad_type) { + case AUTODOC_CLASS: + case AUTODOC_DTOR: + break; + case AUTODOC_CTOR: + Printf(doc, "Class constructor.\n"); + break; + case AUTODOC_STATICFUNC: + Printf(doc, "A class method.\n"); + break; + case AUTODOC_FUNC: + Printf(doc, "A module function.\n"); + break; + case AUTODOC_METHOD: + Printf(doc, "An instance method.\n"); + break; + case AUTODOC_GETTER: + Printf(doc, "Get value of attribute.\n"); + break; + case AUTODOC_SETTER: + Printf(doc, "Set new value for attribute.\n"); + break; + case AUTODOC_NONE: + break; + } + } + + + n = on; + while ( n ) { + String *autodoc = Getattr(n, "feature:autodoc"); + autodoc_l dlevel = autodoc_level(autodoc); + + switch (dlevel) { + case NO_AUTODOC: + case NAMES_AUTODOC: + case TYPES_AUTODOC: + extended = 0; + break; + case STRING_AUTODOC: + extended = 2; + Replaceall( autodoc, "$class", class_name ); + Printv(doc, autodoc, ".", NIL); + break; + case EXTEND_AUTODOC: + case EXTEND_TYPES_AUTODOC: + extended = 1; + break; + } + + + if (extended) { + String *pdocs = Getattr(n, "feature:pdocs"); + if (pdocs) { + Printv(doc, "\n\n", pdocs, NULL); + break; + } + if ( extended == 2 ) break; + } + n = Getattr(n, "sym:nextSibling"); + } + + Delete(full_name); + Delete(class_name); + Delete(super_names); + Delete(methodName); + + return doc; + } + + /* ------------------------------------------------------------ + * convertValue() + * Check if string v can be a Ruby value literal, + * (eg. number or string), or translate it to a Ruby literal. + * ------------------------------------------------------------ */ + String *convertValue(String *v, String *numval, String *stringval, SwigType *type) { + if (stringval) { + return NewStringf("\"%(escape)s\"", stringval); + } + if (numval) { + SwigType *resolved_type = SwigType_typedef_resolve_all(type); + SwigType *unqualified_type = SwigType_strip_qualifiers(resolved_type); + if (Equal(unqualified_type, "bool")) { + Delete(resolved_type); + Delete(unqualified_type); + return NewString(*Char(numval) == '0' ? "false" : "true"); + } + Delete(resolved_type); + Delete(unqualified_type); + if (SwigType_ispointer(type) && Equal(v, "0")) + return NewString("None"); + return Copy(v); + } + if (v && Len(v) > 0) { + if (Equal(v, "NULL") || Equal(v, "nullptr")) + return SwigType_ispointer(type) ? NewString("nil") : NewString("0"); + // FIXME: TRUE and FALSE are not standard and could be defined in other ways + if (Equal(v, "TRUE")) + return NewString("true"); + if (Equal(v, "FALSE")) + return NewString("false"); + } + return 0; + } + +public: + + /* --------------------------------------------------------------------- + * RUBY() + * + * Initialize member data + * --------------------------------------------------------------------- */ + RUBY() : + module(0), + modvar(0), + feature(0), + prefix(0), + current(0), + classes(0), + klass(0), + special_methods(0), + f_directors(0), + f_directors_h(0), + f_directors_helpers(0), + f_begin(0), + f_runtime(0), + f_runtime_h(0), + f_header(0), + f_wrappers(0), + f_init(0), + f_initbeforefunc(0), + useGlobalModule(false), + multipleInheritance(false), + last_mode(AUTODOC_NONE), + last_autodoc(NewString("")) { + current = NO_CPP; + director_prot_ctor_code = NewString(""); + Printv(director_prot_ctor_code, + "if ( $comparison ) { /* subclassed */\n", + " $director_new \n", + "} else {\n", " rb_raise(rb_eRuntimeError,\"accessing abstract class or protected constructor\"); \n", " return Qnil;\n", "}\n", NIL); + director_multiple_inheritance = 0; + directorLanguage(); + } + + /* --------------------------------------------------------------------- + * main() + * + * Parse command line options and initializes variables. + * --------------------------------------------------------------------- */ + + virtual void main(int argc, char *argv[]) { + + int autorename = 0; + + /* Set location of SWIG library */ + SWIG_library_directory("ruby"); + + /* Look for certain command line options */ + for (int i = 1; i < argc; i++) { + if (argv[i]) { + if (strcmp(argv[i], "-initname") == 0) { + if (argv[i + 1]) { + char *name = argv[i + 1]; + feature = NewString(name); + Swig_mark_arg(i); + Swig_mark_arg(i + 1); + i++; + } else { + Swig_arg_error(); + } + } else if (strcmp(argv[i], "-globalmodule") == 0) { + useGlobalModule = true; + Swig_mark_arg(i); + } else if (strcmp(argv[i], "-minherit") == 0) { + multipleInheritance = true; + director_multiple_inheritance = 1; + Swig_mark_arg(i); + } else if (strcmp(argv[i], "-autorename") == 0) { + autorename = 1; + Swig_mark_arg(i); + } else if (strcmp(argv[i], "-noautorename") == 0) { + autorename = 0; + Swig_mark_arg(i); + } else if (strcmp(argv[i], "-prefix") == 0) { + if (argv[i + 1]) { + char *name = argv[i + 1]; + prefix = NewString(name); + Swig_mark_arg(i); + Swig_mark_arg(i + 1); + i++; + } else { + Swig_arg_error(); + } + } else if (strcmp(argv[i], "-help") == 0) { + Printf(stdout, "%s\n", usage); + } else if (strcmp(argv[i], "-cppcast") == 0) { + Printf(stderr, "Deprecated command line option: %s. This option is now always on.\n", argv[i]); + Swig_mark_arg(i); + } else if (strcmp(argv[i], "-nocppcast") == 0) { + Printf(stderr, "Deprecated command line option: %s. This option is no longer supported.\n", argv[i]); + Swig_mark_arg(i); + Exit(EXIT_FAILURE); + } + } + } + + if (autorename) { + /* Turn on the autorename mode */ + Preprocessor_define((DOH *) "SWIG_RUBY_AUTORENAME", 0); + } + + /* Add a symbol to the parser for conditional compilation */ + Preprocessor_define("SWIGRUBY 1", 0); + + SWIG_config_file("ruby.swg"); + allow_overloading(); + } + + /** + * Generate initialization code to define the Ruby module(s), + * accounting for nested modules as necessary. + */ + void defineRubyModule() { + List *modules = Split(module, ':', INT_MAX); + if (modules != 0 && Len(modules) > 0) { + String *mv = 0; + Iterator m; + m = First(modules); + while (m.item) { + if (Len(m.item) > 0) { + if (mv != 0) { + Printv(f_init, tab4, modvar, " = rb_define_module_under(", modvar, ", \"", m.item, "\");\n", NIL); + } else { + Printv(f_init, tab4, modvar, " = rb_define_module(\"", m.item, "\");\n", NIL); + mv = NewString(modvar); + } + } + m = Next(m); + } + Delete(mv); + Delete(modules); + } + } + + void registerMagicMethods() { + + special_methods = NewHash(); + + /* Python->Ruby style special method name. */ + /* Basic */ + Setattr(special_methods, "__repr__", "inspect"); + Setattr(special_methods, "__str__", "to_s"); + Setattr(special_methods, "__cmp__", "<=>"); + Setattr(special_methods, "__hash__", "hash"); + Setattr(special_methods, "__nonzero__", "nonzero?"); + + /* Callable */ + Setattr(special_methods, "__call__", "call"); + + /* Collection */ + Setattr(special_methods, "__len__", "length"); + Setattr(special_methods, "__getitem__", "[]"); + Setattr(special_methods, "__setitem__", "[]="); + + /* Operators */ + Setattr(special_methods, "__add__", "+"); + Setattr(special_methods, "__pos__", "+@"); + Setattr(special_methods, "__sub__", "-"); + Setattr(special_methods, "__neg__", "-@"); + Setattr(special_methods, "__mul__", "*"); + Setattr(special_methods, "__div__", "/"); + Setattr(special_methods, "__mod__", "%"); + Setattr(special_methods, "__lshift__", "<<"); + Setattr(special_methods, "__rshift__", ">>"); + Setattr(special_methods, "__and__", "&"); + Setattr(special_methods, "__or__", "|"); + Setattr(special_methods, "__xor__", "^"); + Setattr(special_methods, "__invert__", "~"); + Setattr(special_methods, "__lt__", "<"); + Setattr(special_methods, "__le__", "<="); + Setattr(special_methods, "__gt__", ">"); + Setattr(special_methods, "__ge__", ">="); + Setattr(special_methods, "__eq__", "=="); + + /* Other numeric */ + Setattr(special_methods, "__divmod__", "divmod"); + Setattr(special_methods, "__pow__", "**"); + Setattr(special_methods, "__abs__", "abs"); + Setattr(special_methods, "__int__", "to_i"); + Setattr(special_methods, "__float__", "to_f"); + Setattr(special_methods, "__coerce__", "coerce"); + } + + /* --------------------------------------------------------------------- + * top() + * --------------------------------------------------------------------- */ + + virtual int top(Node *n) { + + String *mod_docstring = NULL; + + /** + * See if any Ruby module options have been specified as options + * to the %module directive. + */ + Node *swigModule = Getattr(n, "module"); + if (swigModule) { + Node *options = Getattr(swigModule, "options"); + if (options) { + if (Getattr(options, "directors")) { + allow_directors(); + } + if (Getattr(options, "dirprot")) { + allow_dirprot(); + } + if (Getattr(options, "ruby_globalmodule")) { + useGlobalModule = true; + } + if (Getattr(options, "ruby_minherit")) { + multipleInheritance = true; + director_multiple_inheritance = 1; + } + mod_docstring = Getattr(options, "docstring"); + } + } + + /* Set comparison with none for ConstructorToFunction */ + + + setSubclassInstanceCheck(NewStringf("strcmp(rb_obj_classname(self), classname) != 0")); + // 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"); + + if (!outfile) { + Printf(stderr, "Unable to determine outfile\n"); + Exit(EXIT_FAILURE); + } + + f_begin = NewFile(outfile, "w", SWIG_output_files()); + if (!f_begin) { + FileErrorDisplay(outfile); + Exit(EXIT_FAILURE); + } + + f_runtime = NewString(""); + f_init = NewString(""); + f_header = NewString(""); + f_wrappers = NewString(""); + f_directors_h = NewString(""); + f_directors = NewString(""); + f_directors_helpers = NewString(""); + f_initbeforefunc = NewString(""); + + if (Swig_directors_enabled()) { + if (!outfile_h) { + Printf(stderr, "Unable to determine outfile_h\n"); + Exit(EXIT_FAILURE); + } + f_runtime_h = NewFile(outfile_h, "w", SWIG_output_files()); + if (!f_runtime_h) { + FileErrorDisplay(outfile_h); + Exit(EXIT_FAILURE); + } + } + + /* Register file targets with the SWIG file handler */ + Swig_register_filebyname("header", f_header); + Swig_register_filebyname("wrapper", f_wrappers); + Swig_register_filebyname("begin", f_begin); + 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); + Swig_register_filebyname("director_helpers", f_directors_helpers); + Swig_register_filebyname("initbeforefunc", f_initbeforefunc); + + modvar = 0; + current = NO_CPP; + klass = 0; + classes = NewHash(); + + registerMagicMethods(); + + Swig_banner(f_begin); + + Swig_obligatory_macros(f_runtime, "RUBY"); + + if (Swig_directors_enabled()) { + Printf(f_runtime, "#define SWIG_DIRECTORS\n"); + } + + Printf(f_runtime, "\n"); + + /* typedef void *VALUE */ + SwigType *value = NewSwigType(T_VOID); + SwigType_add_pointer(value); + SwigType_typedef(value, "VALUE"); + Delete(value); + + /* Set module name */ + set_module(Char(Getattr(n, "name"))); + + if (Swig_directors_enabled()) { + /* Build a version of the module name for use in a C macro name. */ + String *module_macro = Copy(module); + Replaceall(module_macro, "::", "__"); + + Swig_banner(f_directors_h); + Printf(f_directors_h, "\n"); + Printf(f_directors_h, "#ifndef SWIG_%s_WRAP_H_\n", module_macro); + Printf(f_directors_h, "#define SWIG_%s_WRAP_H_\n\n", module_macro); + Printf(f_directors_h, "namespace Swig {\n"); + Printf(f_directors_h, " class Director;\n"); + Printf(f_directors_h, "}\n\n"); + + Printf(f_directors_helpers, "/* ---------------------------------------------------\n"); + Printf(f_directors_helpers, " * C++ director class helpers\n"); + Printf(f_directors_helpers, " * --------------------------------------------------- */\n\n"); + + Printf(f_directors, "\n\n"); + Printf(f_directors, "/* ---------------------------------------------------\n"); + Printf(f_directors, " * C++ director class methods\n"); + Printf(f_directors, " * --------------------------------------------------- */\n\n"); + if (outfile_h) { + String *filename = Swig_file_filename(outfile_h); + Printf(f_directors, "#include \"%s\"\n\n", filename); + Delete(filename); + } + + Delete(module_macro); + } + + Printf(f_header, "#define SWIG_init Init_%s\n", feature); + Printf(f_header, "#define SWIG_name \"%s\"\n\n", module); + + if (mod_docstring) { + if (Len(mod_docstring)) { + Printf(f_header, "/*\n Document-module: %s\n\n%s\n*/\n", module, mod_docstring); + } + Delete(mod_docstring); + mod_docstring = NULL; + } + + Printf(f_header, "static VALUE %s;\n", modvar); + + /* Start generating the initialization function */ + String* docs = docstring(n, AUTODOC_CLASS); + Printf(f_init, "/*\n%s\n*/", docs ); + Printv(f_init, "\n", "#ifdef __cplusplus\n", "extern \"C\"\n", "#endif\n", "SWIGEXPORT void Init_", feature, "(void) {\n", "size_t i;\n", "\n", NIL); + + Printv(f_init, tab4, "SWIG_InitRuntime();\n", NIL); + + if (!useGlobalModule) + defineRubyModule(); + + Printv(f_init, "\n", "SWIG_InitializeModule(0);\n", "for (i = 0; i < swig_module.size; i++) {\n", "SWIG_define_class(swig_module.types[i]);\n", "}\n", NIL); + Printf(f_init, "\n"); + + /* Initialize code to keep track of objects */ + Printf(f_init, "SWIG_RubyInitializeTrackings();\n"); + + Language::top(n); + + if (Swig_directors_enabled()) { + // Insert director runtime into the f_runtime file (make it occur before %header section) + Swig_insert_file("director_common.swg", f_runtime); + Swig_insert_file("director_guard.swg", f_runtime); + Swig_insert_file("director.swg", f_runtime); + } + + /* Finish off our init function */ + Printf(f_init, "}\n"); + SwigType_emit_type_table(f_runtime, f_wrappers); + + /* Close all of the files */ + Dump(f_runtime, f_begin); + Dump(f_header, f_begin); + + if (Swig_directors_enabled()) { + Dump(f_directors_helpers, f_begin); + Dump(f_directors, f_begin); + Dump(f_directors_h, f_runtime_h); + Printf(f_runtime_h, "\n"); + Printf(f_runtime_h, "#endif\n"); + Delete(f_runtime_h); + } + + Dump(f_wrappers, f_begin); + Dump(f_initbeforefunc, f_begin); + Wrapper_pretty_print(f_init, f_begin); + + Delete(f_header); + Delete(f_wrappers); + Delete(f_init); + Delete(f_initbeforefunc); + Delete(f_runtime); + Delete(f_begin); + + return SWIG_OK; + } + + /* ----------------------------------------------------------------------------- + * importDirective() + * ----------------------------------------------------------------------------- */ + + virtual int importDirective(Node *n) { + String *modname = Getattr(n, "module"); + if (modname) { + if (prefix) { + Insert(modname, 0, prefix); + } + + List *modules = Split(modname, ':', INT_MAX); + if (modules && Len(modules) > 0) { + modname = NewString(""); + String *last = NULL; + Iterator m = First(modules); + while (m.item) { + if (Len(m.item) > 0) { + if (last) { + Append(modname, "/"); + } + Append(modname, m.item); + last = m.item; + } + m = Next(m); + } + Printf(f_init, "rb_require(\"%s\");\n", modname); + Delete(modname); + } + Delete(modules); + } + return Language::importDirective(n); + } + + /* --------------------------------------------------------------------- + * set_module(const char *mod_name) + * + * Sets the module name. Does nothing if it's already set (so it can + * be overridden as a command line option). + *---------------------------------------------------------------------- */ + + void set_module(const char *s) { + String *mod_name = NewString(s); + if (module == 0) { + /* Start with the empty string */ + module = NewString(""); + + if (prefix) { + Insert(mod_name, 0, prefix); + } + + /* Account for nested modules */ + List *modules = Split(mod_name, ':', INT_MAX); + if (modules != 0 && Len(modules) > 0) { + String *last = 0; + Iterator m = First(modules); + while (m.item) { + if (Len(m.item) > 0) { + String *cap = NewString(m.item); + (Char(cap))[0] = (char)toupper((Char(cap))[0]); + if (last != 0) { + Append(module, "::"); + } + Append(module, cap); + last = m.item; + } + m = Next(m); + } + if (last) { + if (feature == 0) { + feature = Copy(last); + } + (Char(last))[0] = (char)toupper((Char(last))[0]); + modvar = NewStringf("m%s", last); + } + } + Delete(modules); + } + Delete(mod_name); + } + + /* -------------------------------------------------------------------------- + * nativeWrapper() + * -------------------------------------------------------------------------- */ + virtual int nativeWrapper(Node *n) { + String *funcname = Getattr(n, "wrap:name"); + Swig_warning(WARN_LANG_NATIVE_UNIMPL, input_file, line_number, "Adding native function %s not supported (ignored).\n", funcname); + return SWIG_NOWRAP; + } + + /** + * Process the comma-separated list of aliases (if any). + */ + void defineAliases(Node *n, const_String_or_char_ptr iname) { + String *aliasv = Getattr(n, "feature:alias"); + if (aliasv) { + List *aliases = Split(aliasv, ',', INT_MAX); + if (aliases && Len(aliases) > 0) { + Iterator alias = First(aliases); + while (alias.item) { + if (Len(alias.item) > 0) { + if (current == NO_CPP) { + if (useGlobalModule) { + Printv(f_init, tab4, "rb_define_alias(rb_cObject, \"", alias.item, "\", \"", iname, "\");\n", NIL); + } else { + Printv(f_init, tab4, "rb_define_alias(rb_singleton_class(", modvar, "), \"", alias.item, "\", \"", iname, "\");\n", NIL); + } + } else if (multipleInheritance) { + Printv(klass->init, tab4, "rb_define_alias(", klass->mImpl, ", \"", alias.item, "\", \"", iname, "\");\n", NIL); + } else { + Printv(klass->init, tab4, "rb_define_alias(", klass->vname, ", \"", alias.item, "\", \"", iname, "\");\n", NIL); + } + } + alias = Next(alias); + } + } + Delete(aliases); + } + } + + /* --------------------------------------------------------------------- + * create_command(Node *n, char *iname) + * + * Creates a new command from a C function. + * iname = Name of function in scripting language + * + * A note about what "protected" and "private" mean in Ruby: + * + * A private method is accessible only within the class or its subclasses, + * and it is callable only in "function form", with 'self' (implicit or + * explicit) as a receiver. + * + * A protected method is callable only from within its class, but unlike + * a private method, it can be called with a receiver other than self, such + * as another instance of the same class. + * --------------------------------------------------------------------- */ + + void create_command(Node *n, const_String_or_char_ptr iname) { + + String *alloc_func = Swig_name_wrapper(iname); + String *wname = Swig_name_wrapper(iname); + if (CPlusPlus) { + Insert(wname, 0, "VALUEFUNC("); + Append(wname, ")"); + } + if (current != NO_CPP) + iname = klass->strip(iname); + if (Getattr(special_methods, iname)) { + iname = GetChar(special_methods, iname); + } + + String *s = NewString(""); + String *temp = NewString(""); + +#ifdef SWIG_PROTECTED_TARGET_METHODS + const char *rb_define_method = is_public(n) ? "rb_define_method" : "rb_define_protected_method"; +#else + const char *rb_define_method = "rb_define_method"; +#endif + switch (current) { + case MEMBER_FUNC: + { + if (multipleInheritance) { + Printv(klass->init, tab4, rb_define_method, "(", klass->mImpl, ", \"", iname, "\", ", wname, ", -1);\n", NIL); + } else { + Printv(klass->init, tab4, rb_define_method, "(", klass->vname, ", \"", iname, "\", ", wname, ", -1);\n", NIL); + } + } + break; + case CONSTRUCTOR_ALLOCATE: + Printv(s, tab4, "rb_define_alloc_func(", klass->vname, ", ", alloc_func, ");\n", NIL); + Replaceall(klass->init, "$allocator", s); + break; + case CONSTRUCTOR_INITIALIZE: + Printv(s, tab4, rb_define_method, "(", klass->vname, ", \"initialize\", ", wname, ", -1);\n", NIL); + Replaceall(klass->init, "$initializer", s); + break; + case MEMBER_VAR: + Append(temp, iname); + /* Check for _set or _get at the end of the name. */ + if (Len(temp) > 4) { + const char *p = Char(temp) + (Len(temp) - 4); + if (strcmp(p, "_set") == 0) { + Delslice(temp, Len(temp) - 4, DOH_END); + Append(temp, "="); + } else if (strcmp(p, "_get") == 0) { + Delslice(temp, Len(temp) - 4, DOH_END); + } + } + if (multipleInheritance) { + Printv(klass->init, tab4, "rb_define_method(", klass->mImpl, ", \"", temp, "\", ", wname, ", -1);\n", NIL); + } else { + Printv(klass->init, tab4, "rb_define_method(", klass->vname, ", \"", temp, "\", ", wname, ", -1);\n", NIL); + } + break; + case STATIC_FUNC: + Printv(klass->init, tab4, "rb_define_singleton_method(", klass->vname, ", \"", iname, "\", ", wname, ", -1);\n", NIL); + break; + case NO_CPP: + if (!useGlobalModule) { + Printv(s, tab4, "rb_define_module_function(", modvar, ", \"", iname, "\", ", wname, ", -1);\n", NIL); + Printv(f_init, s, NIL); + } else { + Printv(s, tab4, "rb_define_global_function(\"", iname, "\", ", wname, ", -1);\n", NIL); + Printv(f_init, s, NIL); + } + break; + case DESTRUCTOR: + case CLASS_CONST: + case STATIC_VAR: + default: + assert(false); // Should not have gotten here for these types + } + + defineAliases(n, iname); + + Delete(temp); + Delete(s); + Delete(wname); + Delete(alloc_func); + } + + /* --------------------------------------------------------------------- + * applyInputTypemap() + * + * Look up the appropriate "in" typemap for this parameter (p), + * substitute the correct strings for the typemap parameters, and dump the + * resulting code to the wrapper file. + * --------------------------------------------------------------------- */ + + Parm *applyInputTypemap(Parm *p, String *source, Wrapper *f, String *symname) { + String *tm; + SwigType *pt = Getattr(p, "type"); + if ((tm = Getattr(p, "tmap:in"))) { + Replaceall(tm, "$input", source); + Replaceall(tm, "$symname", symname); + + if (Getattr(p, "wrap:disown") || (Getattr(p, "tmap:in:disown"))) { + Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN"); + } else { + Replaceall(tm, "$disown", "0"); + } + + Setattr(p, "emit:input", Copy(source)); + Printf(f->code, "%s\n", tm); + p = Getattr(p, "tmap:in:next"); + } else { + Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(pt, 0)); + p = nextSibling(p); + } + return p; + } + + Parm *skipIgnoredArgs(Parm *p) { + while (checkAttribute(p, "tmap:in:numinputs", "0")) { + p = Getattr(p, "tmap:in:next"); + } + return p; + } + + /* --------------------------------------------------------------------- + * marshalInputArgs() + * + * Process all of the arguments passed into the scripting language + * method and convert them into C/C++ function arguments using the + * supplied typemaps. + * --------------------------------------------------------------------- */ + + void marshalInputArgs(Node *n, ParmList *l, int numarg, int numreq, String *kwargs, bool allow_kwargs, Wrapper *f) { + int i; + Parm *p; + String *tm; + String *source; + + source = NewString(""); + + bool ctor_director = (current == CONSTRUCTOR_INITIALIZE && Swig_directorclass(n)); + + /** + * The 'start' value indicates which of the C/C++ function arguments + * produced here corresponds to the first value in Ruby's argv[] array. + * The value of start is either zero or one. If start is zero, then + * the first argument (with name arg1) is based on the value of argv[0]. + * If start is one, then arg1 is based on the value of argv[1]. + */ + int start = (current == MEMBER_FUNC || current == MEMBER_VAR || ctor_director) ? 1 : 0; + + int varargs = emit_isvarargs(l); + + Printf(kwargs, "{ "); + for (i = 0, p = l; i < numarg; i++) { + + p = skipIgnoredArgs(p); + + String *pn = Getattr(p, "name"); + + /* Produce string representation of source argument */ + Clear(source); + + /* First argument is a special case */ + if (i == 0) { + Printv(source, (start == 0) ? "argv[0]" : "self", NIL); + } else { + Printf(source, "argv[%d]", i - start); + } + + if (i >= (numreq)) { /* Check if parsing an optional argument */ + Printf(f->code, " if (argc > %d) {\n", i - start); + } + + /* Record argument name for keyword argument handling */ + if (Len(pn)) { + Printf(kwargs, "\"%s\",", pn); + } else { + Printf(kwargs, "\"arg%d\",", i + 1); + } + + /* Look for an input typemap */ + p = applyInputTypemap(p, source, f, Getattr(n, "name")); + if (i >= numreq) { + Printf(f->code, "}\n"); + } + } + + /* Finish argument marshalling */ + Printf(kwargs, " NULL }"); + if (allow_kwargs) { +// kwarg support not implemented +// Printv(f->locals, tab4, "const char *kwnames[] = ", kwargs, ";\n", NIL); + } + + /* Trailing varargs */ + if (varargs) { + if (p && (tm = Getattr(p, "tmap:in"))) { + Clear(source); + Printf(source, "argv[%d]", i - start); + Replaceall(tm, "$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); + } + + /* --------------------------------------------------------------------- + * insertConstraintCheckingCode(ParmList *l, Wrapper *f) + * + * Checks each of the parameters in the parameter list for a "check" + * typemap and (if it finds one) inserts the typemapping code into + * the function wrapper. + * --------------------------------------------------------------------- */ + + void insertConstraintCheckingCode(ParmList *l, Wrapper *f) { + Parm *p; + String *tm; + for (p = l; p;) { + if ((tm = Getattr(p, "tmap:check"))) { + Printv(f->code, tm, "\n", NIL); + p = Getattr(p, "tmap:check:next"); + } else { + p = nextSibling(p); + } + } + } + + /* --------------------------------------------------------------------- + * insertCleanupCode(ParmList *l, String *cleanup) + * + * Checks each of the parameters in the parameter list for a "freearg" + * typemap and (if it finds one) inserts the typemapping code into + * the function wrapper. + * --------------------------------------------------------------------- */ + + void insertCleanupCode(ParmList *l, String *cleanup) { + String *tm; + for (Parm *p = l; p;) { + if ((tm = Getattr(p, "tmap:freearg"))) { + if (Len(tm) != 0) { + Printv(cleanup, tm, "\n", NIL); + } + p = Getattr(p, "tmap:freearg:next"); + } else { + p = nextSibling(p); + } + } + } + + /* --------------------------------------------------------------------- + * insertArgOutputCode(ParmList *l, String *outarg, int& need_result) + * + * Checks each of the parameters in the parameter list for a "argout" + * typemap and (if it finds one) inserts the typemapping code into + * the function wrapper. + * --------------------------------------------------------------------- */ + + void insertArgOutputCode(ParmList *l, String *outarg, int &need_result) { + String *tm; + for (Parm *p = l; p;) { + if ((tm = Getattr(p, "tmap:argout"))) { + Replaceall(tm, "$result", "vresult"); + Replaceall(tm, "$arg", Getattr(p, "emit:input")); + Replaceall(tm, "$input", Getattr(p, "emit:input")); + + Printv(outarg, tm, "\n", NIL); + need_result += 1; + p = Getattr(p, "tmap:argout:next"); + } else { + p = nextSibling(p); + } + } + } + + /* --------------------------------------------------------------------- + * validIdentifier() + * + * Is this a valid identifier in the scripting language? + * Ruby method names can include any combination of letters, numbers + * and underscores. A Ruby method name may optionally end with + * a question mark ("?"), exclamation point ("!") or equals sign ("="). + * + * Methods whose names end with question marks are, by convention, + * predicate methods that return true or false (e.g. Array#empty?). + * + * Methods whose names end with exclamation points are, by convention, + * called bang methods that modify the instance in place (e.g. Array#sort!). + * + * Methods whose names end with an equals sign are attribute setters + * (e.g. Thread#critical=). + * --------------------------------------------------------------------- */ + + virtual int validIdentifier(String *s) { + char *c = Char(s); + while (*c) { + if (!(isalnum(*c) || (*c == '_') || (*c == '?') || (*c == '!') || (*c == '='))) + return 0; + c++; + } + return 1; + } + + /* --------------------------------------------------------------------- + * functionWrapper() + * + * Create a function declaration and register it with the interpreter. + * --------------------------------------------------------------------- */ + + virtual int functionWrapper(Node *n) { + + String *nodeType; + bool destructor; + + String *symname = Copy(Getattr(n, "sym:name")); + SwigType *returntype = Getattr(n, "type"); + ParmList *l = Getattr(n, "parms"); + int director_method = 0; + String *tm; + + int need_result = 0; + + /* Ruby needs no destructor wrapper */ + if (current == DESTRUCTOR) + return SWIG_NOWRAP; + + nodeType = Getattr(n, "nodeType"); + destructor = (!Cmp(nodeType, "destructor")); + + /* 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 + * overloading). */ + + if (current == CONSTRUCTOR_ALLOCATE && Swig_symbol_isoverloaded(n) && Getattr(n, "sym:nextSibling") != 0) + return SWIG_OK; + + String *overname = 0; + if (Getattr(n, "sym:overloaded")) { + overname = Getattr(n, "sym:overname"); + } else { + if (!addSymbol(symname, n)) + return SWIG_ERROR; + } + + String *cleanup = NewString(""); + String *outarg = NewString(""); + String *kwargs = NewString(""); + Wrapper *f = NewWrapper(); + + /* Rename predicate methods */ + if (GetFlag(n, "feature:predicate")) { + Append(symname, "?"); + } + + /* Rename bang methods */ + if (GetFlag(n, "feature:bang")) { + Append(symname, "!"); + } + + /* Determine the name of the SWIG wrapper function */ + String *wname = Swig_name_wrapper(symname); + if (overname && current != CONSTRUCTOR_ALLOCATE) { + Append(wname, overname); + } + + /* Emit arguments */ + if (current != CONSTRUCTOR_ALLOCATE) { + emit_parameter_variables(l, f); + } + + /* Attach standard typemaps */ + if (current != CONSTRUCTOR_ALLOCATE) { + emit_attach_parmmaps(l, f); + } + Setattr(n, "wrap:parms", l); + + /* Get number of arguments */ + int numarg = emit_num_arguments(l); + int numreq = emit_num_required(l); + int varargs = emit_isvarargs(l); + bool allow_kwargs = GetFlag(n, "feature:kwargs") ? true : false; + + bool ctor_director = (current == CONSTRUCTOR_INITIALIZE && Swig_directorclass(n)); + int start = (current == MEMBER_FUNC || current == MEMBER_VAR || ctor_director) ? 1 : 0; + + /* Now write the wrapper function itself */ + if (current == CONSTRUCTOR_ALLOCATE) { + Printv(f->def, "SWIGINTERN VALUE\n", NIL); + Printf(f->def, "#ifdef HAVE_RB_DEFINE_ALLOC_FUNC\n"); + Printv(f->def, wname, "(VALUE self)\n", NIL); + Printf(f->def, "#else\n"); + Printv(f->def, wname, "(int argc, VALUE *argv, VALUE self)\n", NIL); + Printf(f->def, "#endif\n"); + Printv(f->def, "{\n", NIL); + } else if (current == CONSTRUCTOR_INITIALIZE) { + Printv(f->def, "SWIGINTERN VALUE\n", wname, "(int argc, VALUE *argv, VALUE self) {", NIL); + if (!varargs) { + Printf(f->code, "if ((argc < %d) || (argc > %d)) ", numreq - start, numarg - start); + } else { + Printf(f->code, "if (argc < %d) ", numreq - start); + } + Printf(f->code, "{rb_raise(rb_eArgError, \"wrong # of arguments(%%d for %d)\",argc); SWIG_fail;}\n", numreq - start); + } else { + + if ( current == NO_CPP ) + { + String* docs = docstring(n, AUTODOC_FUNC); + Printf(f_wrappers, "%s", docs); + Delete(docs); + } + + Printv(f->def, "SWIGINTERN VALUE\n", wname, "(int argc, VALUE *argv, VALUE self) {", NIL); + if (!varargs) { + Printf(f->code, "if ((argc < %d) || (argc > %d)) ", numreq - start, numarg - start); + } else { + Printf(f->code, "if (argc < %d) ", numreq - start); + } + Printf(f->code, "{rb_raise(rb_eArgError, \"wrong # of arguments(%%d for %d)\",argc); SWIG_fail;}\n", numreq - start); + } + + /* Now walk the function parameter list and generate code */ + /* to get arguments */ + if (current != CONSTRUCTOR_ALLOCATE) { + marshalInputArgs(n, l, numarg, numreq, kwargs, allow_kwargs, f); + } + // FIXME? + if (ctor_director) { + numarg--; + numreq--; + } + + /* Insert constraint checking code */ + insertConstraintCheckingCode(l, f); + + /* Insert cleanup code */ + insertCleanupCode(l, cleanup); + + /* Insert argument output code */ + insertArgOutputCode(l, outarg, need_result); + + /* if the object is a director, and the method call originated from its + * underlying Ruby object, resolve the call by going up the c++ + * inheritance chain. otherwise try to resolve the method in Ruby. + * 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. + + director_method = is_member_director(n) && !is_smart_pointer() && !destructor; + if (director_method) { + Wrapper_add_local(f, "director", "Swig::Director *director = 0"); + Printf(f->code, "director = dynamic_cast<Swig::Director *>(arg1);\n"); + Wrapper_add_local(f, "upcall", "bool upcall = false"); + Append(f->code, "upcall = (director && (director->swig_get_self() == self));\n"); + } + + /* Now write code to make the function call */ + if (current != CONSTRUCTOR_ALLOCATE) { + if (current == CONSTRUCTOR_INITIALIZE) { + Node *pn = Swig_methodclass(n); + String *symname = Getattr(pn, "sym:name"); + String *action = Getattr(n, "wrap:action"); + if (Swig_directors_enabled()) { + String *classname = NewStringf("const char *classname SWIGUNUSED = \"%s::%s\"", module, symname); + Wrapper_add_local(f, "classname", classname); + } + if (action) { + SwigType *smart = Getattr(pn, "smart"); + String *result_name = NewStringf("%s%s", smart ? "smart" : "", Swig_cresult_name()); + if (smart) { + String *result_var = NewStringf("%s *%s = 0", SwigType_namestr(smart), result_name); + Wrapper_add_local(f, result_name, result_var); + Printf(action, "\n%s = new %s(%s);", result_name, SwigType_namestr(smart), Swig_cresult_name()); + } + Printf(action, "\nDATA_PTR(self) = %s;", result_name); + if (GetFlag(pn, "feature:trackobjects")) { + Printf(action, "\nSWIG_RubyAddTracking(%s, self);", result_name); + } + Delete(result_name); + } + } + + /* Emit the function call */ + if (director_method) { + Printf(f->code, "try {\n"); + } + + Setattr(n, "wrap:name", wname); + + Swig_director_emit_dynamic_cast(n, f); + String *actioncode = emit_action(n); + + if (director_method) { + Printf(actioncode, "} catch (Swig::DirectorException& e) {\n"); + Printf(actioncode, " rb_exc_raise(e.getError());\n"); + Printf(actioncode, " SWIG_fail;\n"); + Printf(actioncode, "}\n"); + } + + /* Return value if necessary */ + if (SwigType_type(returntype) != T_VOID && current != CONSTRUCTOR_INITIALIZE) { + need_result = 1; + if (GetFlag(n, "feature:predicate")) { + Printv(actioncode, tab4, "vresult = (", Swig_cresult_name(), " ? Qtrue : Qfalse);\n", NIL); + } else { + tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode); + actioncode = 0; + if (tm) { + Replaceall(tm, "$result", "vresult"); + + if (GetFlag(n, "feature:new")) + Replaceall(tm, "$owner", "SWIG_POINTER_OWN"); + else + Replaceall(tm, "$owner", "0"); + + // Unwrap return values that are director classes so that the original Ruby object is returned instead. + if (Swig_director_can_unwrap(n)) { + Wrapper_add_local(f, "director", "Swig::Director *director = 0"); + Printf(f->code, "director = dynamic_cast<Swig::Director *>(%s);\n", Swig_cresult_name()); + Printf(f->code, "if (director) {\n"); + Printf(f->code, " vresult = director->swig_get_self();\n"); + Printf(f->code, "} else {\n"); + Printf(f->code, "%s\n", tm); + Printf(f->code, "}\n"); + director_method = 0; + } else { + Printf(f->code, "%s\n", tm); + } + + Delete(tm); + } else { + Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s.\n", SwigType_str(returntype, 0)); + } + } + } + if (actioncode) { + Append(f->code, actioncode); + Delete(actioncode); + } + emit_return_variable(n, returntype, f); + } + + /* Extra code needed for new and initialize methods */ + if (current == CONSTRUCTOR_ALLOCATE) { + Node *pn = Swig_methodclass(n); + SwigType *smart = Getattr(pn, "smart"); + SwigType *psmart = smart ? Copy(smart) : 0; + if (psmart) + SwigType_add_pointer(psmart); + SwigType *classtype = psmart ? psmart : returntype; + need_result = 1; + Printf(f->code, "VALUE vresult = SWIG_NewClassInstance(self, SWIGTYPE%s);\n", Char(SwigType_manglestr(classtype))); + Printf(f->code, "#ifndef HAVE_RB_DEFINE_ALLOC_FUNC\n"); + Printf(f->code, "rb_obj_call_init(vresult, argc, argv);\n"); + Printf(f->code, "#endif\n"); + Delete(psmart); + } else if (current == CONSTRUCTOR_INITIALIZE) { + need_result = 1; + } + else + { + if ( need_result > 1 ) { + if ( SwigType_type(returntype) == T_VOID ) + Printf(f->code, "vresult = rb_ary_new();\n"); + else + { + Printf(f->code, "if (vresult == Qnil) vresult = rb_ary_new();\n"); + Printf(f->code, "else vresult = SWIG_Ruby_AppendOutput( " + "rb_ary_new(), vresult);\n"); + } + } + } + + /* Dump argument output code; */ + Printv(f->code, outarg, NIL); + + /* Dump the argument cleanup code */ + int need_cleanup = (current != CONSTRUCTOR_ALLOCATE) && (Len(cleanup) != 0); + if (need_cleanup) { + Printv(f->code, cleanup, NIL); + } + + + /* Look for any remaining cleanup. This processes the %newobject directive */ + if (current != CONSTRUCTOR_ALLOCATE && GetFlag(n, "feature:new")) { + tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0); + if (tm) { + Printv(f->code, tm, "\n", NIL); + Delete(tm); + } + } + + /* Special processing on return value. */ + tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0); + if (tm) { + Printv(f->code, tm, NIL); + Delete(tm); + } + + if (director_method) { + if ((tm = Swig_typemap_lookup("directorfree", n, Swig_cresult_name(), 0))) { + Replaceall(tm, "$input", Swig_cresult_name()); + Replaceall(tm, "$result", "vresult"); + Printf(f->code, "%s\n", tm); + } + } + + + /* Wrap things up (in a manner of speaking) */ + if (need_result) { + if (current == CONSTRUCTOR_ALLOCATE) { + Printv(f->code, tab4, "return vresult;\n", NIL); + } else if (current == CONSTRUCTOR_INITIALIZE) { + Printv(f->code, tab4, "return self;\n", NIL); + Printv(f->code, "fail:\n", NIL); + if (need_cleanup) { + Printv(f->code, cleanup, NIL); + } + Printv(f->code, tab4, "return Qnil;\n", NIL); + } else { + Wrapper_add_local(f, "vresult", "VALUE vresult = Qnil"); + Printv(f->code, tab4, "return vresult;\n", NIL); + Printv(f->code, "fail:\n", NIL); + if (need_cleanup) { + Printv(f->code, cleanup, NIL); + } + Printv(f->code, tab4, "return Qnil;\n", NIL); + } + } else { + Printv(f->code, tab4, "return Qnil;\n", NIL); + Printv(f->code, "fail:\n", NIL); + if (need_cleanup) { + Printv(f->code, cleanup, NIL); + } + Printv(f->code, tab4, "return Qnil;\n", NIL); + } + + Printf(f->code, "}\n"); + + /* Substitute the cleanup code */ + Replaceall(f->code, "$cleanup", cleanup); + + bool isvoid = !Cmp(returntype, "void"); + Replaceall(f->code, "$isvoid", isvoid ? "1" : "0"); + + /* Substitute the function name */ + Replaceall(f->code, "$symname", symname); + + /* Emit the function */ + Wrapper_print(f, f_wrappers); + + /* Now register the function with the interpreter */ + if (!Swig_symbol_isoverloaded(n)) { + create_command(n, symname); + } else { + if (current == CONSTRUCTOR_ALLOCATE) { + create_command(n, symname); + } else { + if (!Getattr(n, "sym:nextSibling")) + dispatchFunction(n); + } + } + + Delete(kwargs); + Delete(cleanup); + Delete(outarg); + DelWrapper(f); + Delete(symname); + + return SWIG_OK; + } + + /* ------------------------------------------------------------ + * dispatchFunction() + * ------------------------------------------------------------ */ + + void dispatchFunction(Node *n) { + /* Last node in overloaded chain */ + + int maxargs; + String *tmp = NewString(""); + String *dispatch = Swig_overload_dispatch(n, "return %s(nargs, args, self);", &maxargs); + + /* Generate a dispatch wrapper for all overloaded functions */ + + Wrapper *f = NewWrapper(); + String *symname = Getattr(n, "sym:name"); + String *wname = Swig_name_wrapper(symname); + + Printv(f->def, "SWIGINTERN VALUE ", wname, "(int nargs, VALUE *args, VALUE self) {", NIL); + + Wrapper_add_local(f, "argc", "int argc"); + bool ctor_director = (current == CONSTRUCTOR_INITIALIZE && Swig_directorclass(n)); + if (current == MEMBER_FUNC || current == MEMBER_VAR || ctor_director) { + Printf(tmp, "VALUE argv[%d]", maxargs + 1); + } else { + Printf(tmp, "VALUE argv[%d]", maxargs); + } + Wrapper_add_local(f, "argv", tmp); + Wrapper_add_local(f, "ii", "int ii"); + + if (current == MEMBER_FUNC || current == MEMBER_VAR || ctor_director) { + maxargs += 1; + Printf(f->code, "argc = nargs + 1;\n"); + Printf(f->code, "argv[0] = self;\n"); + Printf(f->code, "if (argc > %d) SWIG_fail;\n", maxargs); + Printf(f->code, "for (ii = 1; (ii < argc); ++ii) {\n"); + Printf(f->code, "argv[ii] = args[ii-1];\n"); + Printf(f->code, "}\n"); + } else { + Printf(f->code, "argc = nargs;\n"); + Printf(f->code, "if (argc > %d) SWIG_fail;\n", maxargs); + Printf(f->code, "for (ii = 0; (ii < argc); ++ii) {\n"); + Printf(f->code, "argv[ii] = args[ii];\n"); + Printf(f->code, "}\n"); + } + + Replaceall(dispatch, "$args", "nargs, args, self"); + Printv(f->code, dispatch, "\n", NIL); + + + + // Generate prototype list, go to first node + Node *sibl = n; + + while (Getattr(sibl, "sym:previousSibling")) + sibl = Getattr(sibl, "sym:previousSibling"); // go all the way up + + // Constructors will be treated specially + String *siblNodeType = Getattr(sibl, "nodeType"); + const bool isCtor = (Equal(siblNodeType, "constructor")); + const bool isMethod = (Equal(siblNodeType, "cdecl") && GetFlag(sibl, "ismember") && !isCtor); + + // Construct real method name + String* methodName = NewString(""); + if ( isMethod ) { + // Sometimes a method node has no parent (SF#3034054). + // This value is used in an exception message, so just skip the class + // name in this case so at least we don't segfault. This is probably + // just working around a problem elsewhere though. + Node *parent_node = parentNode(sibl); + if (parent_node) + Printv( methodName, Getattr(parent_node,"sym:name"), ".", NIL ); + } + Append( methodName, Getattr(sibl,"sym:name" ) ); + if ( isCtor ) Append( methodName, ".new" ); + + // Generate prototype list + String *protoTypes = NewString(""); + do { + Append( protoTypes, "\n\" "); + if (!isCtor && !Equal(siblNodeType, "using")) { + SwigType *type = SwigType_str(Getattr(sibl, "type"), NULL); + Printv(protoTypes, type, " ", NIL); + Delete(type); + } + Printv(protoTypes, methodName, NIL ); + Parm* p = Getattr(sibl, "wrap:parms"); + if (p && (current == MEMBER_FUNC || current == MEMBER_VAR || + ctor_director) ) + p = nextSibling(p); // skip self + Append( protoTypes, "(" ); + while(p) + { + Append( protoTypes, SwigType_str(Getattr(p,"type"), Getattr(p,"name")) ); + if ( ( p = nextSibling(p)) ) Append(protoTypes, ", "); + } + Append( protoTypes, ")\\n\"" ); + } while ((sibl = Getattr(sibl, "sym:nextSibling"))); + + Append(f->code, "fail:\n"); + Printf(f->code, "Ruby_Format_OverloadedError( argc, %d, \"%s\", %s);\n", + maxargs, methodName, protoTypes); + Append(f->code, "\nreturn Qnil;\n"); + + Delete(methodName); + Delete(protoTypes); + + Printv(f->code, "}\n", NIL); + Wrapper_print(f, f_wrappers); + create_command(n, Char(symname)); + + DelWrapper(f); + Delete(dispatch); + Delete(tmp); + Delete(wname); + } + + /* --------------------------------------------------------------------- + * variableWrapper() + * --------------------------------------------------------------------- */ + + virtual int variableWrapper(Node *n) { + String* docs = docstring(n, AUTODOC_GETTER); + Printf(f_wrappers, "%s", docs); + Delete(docs); + + + char *name = GetChar(n, "name"); + char *iname = GetChar(n, "sym:name"); + SwigType *t = Getattr(n, "type"); + String *tm; + String *getfname, *setfname; + Wrapper *getf, *setf; + int assignable = !is_immutable(n); + + // Determine whether virtual global variables shall be used + // which have different getter and setter signatures, + // see https://docs.ruby-lang.org/en/2.6.0/extension_rdoc.html#label-Global+Variables+Shared+Between+C+and+Ruby + const bool use_virtual_var = (current == NO_CPP && useGlobalModule); + + getf = NewWrapper(); + setf = NewWrapper(); + + /* create getter */ + int addfail = 0; + String *getname = Swig_name_get(NSPACE_TODO, iname); + getfname = Swig_name_wrapper(getname); + Setattr(n, "wrap:name", getfname); + Printv(getf->def, "SWIGINTERN VALUE\n", getfname, "(", NIL); + Printf(getf->def, (use_virtual_var) ? "ID id, VALUE *data" : "VALUE self"); + Printf(getf->def, ") {"); + Wrapper_add_local(getf, "_val", "VALUE _val"); + + tm = Swig_typemap_lookup("varout", n, name, 0); + if (tm) { + Replaceall(tm, "$result", "_val"); + /* Printv(getf->code,tm, NIL); */ + addfail = emit_action_code(n, getf->code, tm); + } else { + Swig_warning(WARN_TYPEMAP_VAROUT_UNDEF, input_file, line_number, "Unable to read variable of type %s\n", SwigType_str(t, 0)); + } + Printv(getf->code, tab4, "return _val;\n", NIL); + if (addfail) { + Append(getf->code, "fail:\n"); + Append(getf->code, " return Qnil;\n"); + } + Append(getf->code, "}\n"); + + Wrapper_print(getf, f_wrappers); + + if (!assignable) { + setfname = NewString("(rb_gvar_setter_t *)NULL"); + } else { + /* create setter */ + String* docs = docstring(n, AUTODOC_SETTER); + Printf(f_wrappers, "%s", docs); + Delete(docs); + + String *setname = Swig_name_set(NSPACE_TODO, iname); + setfname = Swig_name_wrapper(setname); + Setattr(n, "wrap:name", setfname); + Printf(setf->def, "SWIGINTERN "); + if (use_virtual_var) { + Printv(setf->def, "void\n", setfname, "(VALUE _val, ID id, VALUE *data) {", NIL); + } else { + Printv(setf->def, "VALUE\n", setfname, "(VALUE self, VALUE _val) {", NIL); + } + tm = Swig_typemap_lookup("varin", n, name, 0); + if (tm) { + Replaceall(tm, "$input", "_val"); + /* Printv(setf->code,tm,"\n",NIL); */ + emit_action_code(n, setf->code, tm); + } else { + Swig_warning(WARN_TYPEMAP_VARIN_UNDEF, input_file, line_number, "Unable to set variable of type %s\n", SwigType_str(t, 0)); + } + if (use_virtual_var) { + Printf(setf->code, "fail:\n"); + Printv(setf->code, tab4, "return;\n", NIL); + } else { + Printv(setf->code, tab4, "return _val;\n", NIL); + Printf(setf->code, "fail:\n"); + Printv(setf->code, tab4, "return Qnil;\n", NIL); + } + Printf(setf->code, "}\n"); + Wrapper_print(setf, f_wrappers); + Delete(setname); + } + + /* define accessor methods */ + Insert(getfname, 0, "VALUEFUNC("); + Append(getfname, ")"); + Insert(setfname, 0, (use_virtual_var) ? "SWIG_RUBY_VOID_ANYARGS_FUNC(" : "VALUEFUNC("); + Append(setfname, ")"); + + String *s = NewString(""); + switch (current) { + case STATIC_VAR: + /* C++ class variable */ + Printv(s, tab4, "rb_define_singleton_method(", klass->vname, ", \"", klass->strip(iname), "\", ", getfname, ", 0);\n", NIL); + if (assignable) { + Printv(s, tab4, "rb_define_singleton_method(", klass->vname, ", \"", klass->strip(iname), "=\", ", setfname, ", 1);\n", NIL); + } + Printv(klass->init, s, NIL); + break; + default: + /* C global variable */ + /* wrapped in Ruby module attribute */ + assert(current == NO_CPP); + if (!useGlobalModule) { + Printv(s, tab4, "rb_define_singleton_method(", modvar, ", \"", iname, "\", ", getfname, ", 0);\n", NIL); + if (assignable) { + Printv(s, tab4, "rb_define_singleton_method(", modvar, ", \"", iname, "=\", ", setfname, ", 1);\n", NIL); + } + } else { + Printv(s, tab4, "rb_define_virtual_variable(\"$", iname, "\", ", getfname, ", ", setfname, ");\n", NIL); + } + Printv(f_init, s, NIL); + Delete(s); + break; + } + Delete(getname); + Delete(getfname); + Delete(setfname); + DelWrapper(setf); + DelWrapper(getf); + return SWIG_OK; + } + + + /* --------------------------------------------------------------------- + * validate_const_name(char *name) + * + * Validate constant name. + * --------------------------------------------------------------------- */ + + char *validate_const_name(char *name, const char *reason) { + if (!name || name[0] == '\0') + return name; + + if (isupper(name[0])) + return name; + + if (islower(name[0])) { + name[0] = (char)toupper(name[0]); + Swig_warning(WARN_RUBY_WRONG_NAME, input_file, line_number, "Wrong %s name (corrected to `%s')\n", reason, name); + return name; + } + + Swig_warning(WARN_RUBY_WRONG_NAME, input_file, line_number, "Wrong %s name %s\n", reason, name); + + return name; + } + + /* --------------------------------------------------------------------- + * constantWrapper() + * --------------------------------------------------------------------- */ + + virtual int constantWrapper(Node *n) { + Swig_require("constantWrapper", n, "*sym:name", "type", "value", NIL); + + char *iname = GetChar(n, "sym:name"); + SwigType *type = Getattr(n, "type"); + String *value = Getattr(n, "value"); + + if (current == CLASS_CONST) { + iname = klass->strip(iname); + } + validate_const_name(iname, "constant"); + SetChar(n, "sym:name", iname); + + /* Special hook for member pointer */ + if (SwigType_type(type) == T_MPOINTER) { + String *wname = Swig_name_wrapper(iname); + Printf(f_header, "static %s = %s;\n", SwigType_str(type, wname), value); + value = Char(wname); + } + String *tm = Swig_typemap_lookup("constant", n, value, 0); + if (!tm) + tm = Swig_typemap_lookup("constcode", n, value, 0); + if (tm) { + Replaceall(tm, "$symname", iname); + Replaceall(tm, "$value", value); + if (current == CLASS_CONST) { + if (multipleInheritance) { + Replaceall(tm, "$module", klass->mImpl); + Printv(klass->init, tm, "\n", NIL); + } else { + Replaceall(tm, "$module", klass->vname); + Printv(klass->init, tm, "\n", NIL); + } + } else { + if (!useGlobalModule) { + Replaceall(tm, "$module", modvar); + } else { + Replaceall(tm, "$module", "rb_cObject"); + } + Printf(f_init, "%s\n", tm); + } + } else { + Swig_warning(WARN_TYPEMAP_CONST_UNDEF, input_file, line_number, "Unsupported constant value %s = %s\n", SwigType_str(type, 0), value); + } + Swig_restore(n); + return SWIG_OK; + } + + /* ----------------------------------------------------------------------------- + * classDeclaration() + * + * Records information about classes---even classes that might be defined in + * other modules referenced by %import. + * ----------------------------------------------------------------------------- */ + + virtual int classDeclaration(Node *n) { + if (!Getattr(n, "feature:onlychildren")) { + String *name = Getattr(n, "name"); + String *symname = Getattr(n, "sym:name"); + + String *namestr = SwigType_namestr(name); + klass = RCLASS(classes, Char(namestr)); + if (!klass) { + klass = new RClass(); + String *valid_name = NewString(symname ? symname : namestr); + validate_const_name(Char(valid_name), "class"); + klass->set_name(namestr, symname, valid_name); + SET_RCLASS(classes, Char(namestr), klass); + Delete(valid_name); + } + Delete(namestr); + } + return Language::classDeclaration(n); + } + + /** + * Process the comma-separated list of mixed-in module names (if any). + */ + void includeRubyModules(Node *n) { + String *mixin = Getattr(n, "feature:mixin"); + if (mixin) { + List *modules = Split(mixin, ',', INT_MAX); + if (modules && Len(modules) > 0) { + Iterator mod = First(modules); + while (mod.item) { + if (Len(mod.item) > 0) { + Printf(klass->init, "rb_include_module(%s, rb_eval_string(\"%s\"));\n", klass->vname, mod.item); + } + mod = Next(mod); + } + } + Delete(modules); + } + } + + void handleBaseClasses(Node *n) { + List *baselist = Getattr(n, "bases"); + if (baselist && Len(baselist)) { + Iterator base = First(baselist); + while (base.item && GetFlag(base.item, "feature:ignore")) { + base = Next(base); + } + while (base.item) { + String *basename = Getattr(base.item, "name"); + String *basenamestr = SwigType_namestr(basename); + RClass *super = RCLASS(classes, Char(basenamestr)); + Delete(basenamestr); + if (super) { + SwigType *btype = NewString(basename); + SwigType_add_pointer(btype); + SwigType_remember(btype); + SwigType *smart = Getattr(base.item, "smart"); + SwigType *psmart = smart ? Copy(smart) : 0; + if (psmart) { + SwigType_add_pointer(psmart); + SwigType_remember(psmart); + } + String *bmangle = SwigType_manglestr(psmart ? psmart : btype); + if (multipleInheritance) { + Insert(bmangle, 0, "((swig_class *) SWIGTYPE"); + Append(bmangle, "->clientdata)->mImpl"); + Printv(klass->init, "rb_include_module(", klass->mImpl, ", ", bmangle, ");\n", NIL); + } else { + Insert(bmangle, 0, "((swig_class *) SWIGTYPE"); + Append(bmangle, "->clientdata)->klass"); + Replaceall(klass->init, "$super", bmangle); + } + Delete(bmangle); + Delete(psmart); + Delete(btype); + } + base = Next(base); + while (base.item && GetFlag(base.item, "feature:ignore")) { + base = Next(base); + } + if (!multipleInheritance) { + /* Warn about multiple inheritance for additional base class(es) */ + while (base.item) { + if (GetFlag(base.item, "feature:ignore")) { + base = Next(base); + continue; + } + String *proxyclassname = SwigType_str(Getattr(n, "classtypeobj"), 0); + String *baseclassname = SwigType_str(Getattr(base.item, "name"), 0); + Swig_warning(WARN_RUBY_MULTIPLE_INHERITANCE, Getfile(n), Getline(n), + "Warning for %s, base %s ignored. Multiple inheritance is not supported in Ruby.\n", proxyclassname, baseclassname); + base = Next(base); + } + } + } + } + } + + /** + * Check to see if a %markfunc was specified. + */ + void handleMarkFuncDirective(Node *n) { + String *markfunc = Getattr(n, "feature:markfunc"); + if (markfunc) { + Printf(klass->init, "SwigClass%s.mark = (void (*)(void *)) %s;\n", klass->name, markfunc); + } else { + Printf(klass->init, "SwigClass%s.mark = 0;\n", klass->name); + } + } + + /** + * Check to see if a %freefunc was specified. + */ + void handleFreeFuncDirective(Node *n) { + String *freefunc = Getattr(n, "feature:freefunc"); + if (freefunc) { + Printf(klass->init, "SwigClass%s.destroy = (void (*)(void *)) %s;\n", klass->name, freefunc); + } else { + if (klass->destructor_defined) { + Printf(klass->init, "SwigClass%s.destroy = (void (*)(void *)) free_%s;\n", klass->name, klass->mname); + } + } + } + + /** + * Check to see if tracking is enabled for this class. + */ + void handleTrackDirective(Node *n) { + int trackObjects = GetFlag(n, "feature:trackobjects"); + if (trackObjects) { + Printf(klass->init, "SwigClass%s.trackObjects = 1;\n", klass->name); + } else { + Printf(klass->init, "SwigClass%s.trackObjects = 0;\n", klass->name); + } + } + + /* ---------------------------------------------------------------------- + * classHandler() + * ---------------------------------------------------------------------- */ + + virtual int classHandler(Node *n) { + String* docs = docstring(n, AUTODOC_CLASS); + Printf(f_wrappers, "%s", docs); + Delete(docs); + + String *name = Getattr(n, "name"); + String *symname = Getattr(n, "sym:name"); + String *namestr = SwigType_namestr(name); // does template expansion + + klass = RCLASS(classes, Char(namestr)); + assert(klass != 0); + Delete(namestr); + String *valid_name = NewString(symname); + validate_const_name(Char(valid_name), "class"); + + Clear(klass->type); + Printv(klass->type, Getattr(n, "classtype"), NIL); + Printv(f_wrappers, "static swig_class SwigClass", valid_name, ";\n\n", NIL); + Printv(klass->init, "\n", tab4, NIL); + + if (!useGlobalModule) { + Printv(klass->init, klass->vname, " = rb_define_class_under(", modvar, ", \"", klass->name, "\", $super);\n", NIL); + } else { + Printv(klass->init, klass->vname, " = rb_define_class(\"", klass->name, + "\", $super);\n", NIL); + } + + if (multipleInheritance) { + Printv(klass->init, klass->mImpl, " = rb_define_module_under(", klass->vname, ", \"Impl\");\n", NIL); + } + + SwigType *tt = NewString(name); + SwigType_add_pointer(tt); + SwigType_remember(tt); + SwigType *smart = Getattr(n, "smart"); + SwigType *psmart = smart ? Copy(smart) : 0; + if (psmart) { + SwigType_add_pointer(psmart); + SwigType_remember(psmart); + } + String *tm = SwigType_manglestr(psmart ? psmart : tt); + Printf(klass->init, "SWIG_TypeClientData(SWIGTYPE%s, (void *) &SwigClass%s);\n", tm, valid_name); + Delete(tm); + Delete(psmart); + Delete(tt); + Delete(valid_name); + + includeRubyModules(n); + + Printv(klass->init, "$allocator", NIL); + Printv(klass->init, "$initializer", NIL); + + Language::classHandler(n); + + handleBaseClasses(n); + handleMarkFuncDirective(n); + handleFreeFuncDirective(n); + handleTrackDirective(n); + + if (multipleInheritance) { + Printv(klass->init, "rb_include_module(", klass->vname, ", ", klass->mImpl, ");\n", NIL); + } + + String *s = NewString(""); + Printv(s, tab4, "rb_undef_alloc_func(", klass->vname, ");\n", NIL); + Replaceall(klass->init, "$allocator", s); + Replaceall(klass->init, "$initializer", ""); + + if (GetFlag(n, "feature:exceptionclass")) { + Replaceall(klass->init, "$super", "rb_eRuntimeError"); + } else { + Replaceall(klass->init, "$super", "rb_cObject"); + } + Delete(s); + + Printv(f_init, klass->init, NIL); + klass = 0; + return SWIG_OK; + } + + /* ---------------------------------------------------------------------- + * memberfunctionHandler() + * + * Method for adding C++ member function + * + * By default, we're going to create a function of the form : + * + * Foo_bar(this,args) + * + * Where Foo is the classname, bar is the member name and the this pointer + * is explicitly attached to the beginning. + * + * The renaming only applies to the member function part, not the full + * classname. + * + * --------------------------------------------------------------------- */ + + virtual int memberfunctionHandler(Node *n) { + current = MEMBER_FUNC; + + String* docs = docstring(n, AUTODOC_METHOD); + Printf(f_wrappers, "%s", docs); + Delete(docs); + + Language::memberfunctionHandler(n); + current = NO_CPP; + return SWIG_OK; + } + + /* --------------------------------------------------------------------- + * constructorHandler() + * + * Method for adding C++ member constructor + * -------------------------------------------------------------------- */ + + void set_director_ctor_code(Node *n) { + /* director ctor code is specific for each class */ + Delete(director_prot_ctor_code); + director_prot_ctor_code = NewString(""); + Node *pn = Swig_methodclass(n); + String *symname = Getattr(pn, "sym:name"); + String *name = Copy(symname); + char *cname = Char(name); + if (cname) + cname[0] = (char)toupper(cname[0]); + Printv(director_prot_ctor_code, + "if ( $comparison ) { /* subclassed */\n", + " $director_new \n", + "} else {\n", " rb_raise(rb_eNameError,\"accessing abstract class or protected constructor\"); \n", " return Qnil;\n", "}\n", NIL); + Delete(director_ctor_code); + director_ctor_code = NewString(""); + Printv(director_ctor_code, "if ( $comparison ) { /* subclassed */\n", " $director_new \n", "} else {\n", " $nondirector_new \n", "}\n", NIL); + Delete(name); + } + + virtual int constructorHandler(Node *n) { + int use_director = Swig_directorclass(n); + if (use_director) { + set_director_ctor_code(n); + } + + /* First wrap the allocate method */ + current = CONSTRUCTOR_ALLOCATE; + Swig_name_register("construct", "%n%c_allocate"); + + Language::constructorHandler(n); + + String* docs = docstring(n, AUTODOC_CTOR); + Printf(f_wrappers, "%s", docs); + Delete(docs); + + /* + * 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("ruby: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, n); + 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("construct", "new_%n%c"); + Language::constructorHandler(n); + + /* Restore original parameter list */ + Delattr(n, "wrap:self"); + Swig_restore(n); + + /* Done */ + Swig_name_unregister("construct"); + current = NO_CPP; + klass->constructor_defined = 1; + return SWIG_OK; + } + + virtual int copyconstructorHandler(Node *n) { + int use_director = Swig_directorclass(n); + if (use_director) { + set_director_ctor_code(n); + } + + /* First wrap the allocate method */ + current = CONSTRUCTOR_ALLOCATE; + Swig_name_register("construct", "%n%c_allocate"); + + return Language::copyconstructorHandler(n); + } + + + /* --------------------------------------------------------------------- + * destructorHandler() + * -------------------------------------------------------------------- */ + + virtual int destructorHandler(Node *n) { + + /* Do no spit free function if user defined his own for this class */ + Node *pn = Swig_methodclass(n); + String *freefunc = Getattr(pn, "feature:freefunc"); + if (freefunc) return SWIG_OK; + + current = DESTRUCTOR; + Language::destructorHandler(n); + + freefunc = NewString(""); + String *freebody = NewString(""); + String *pname0 = Swig_cparm_name(0, 0); + + Printv(freefunc, "free_", klass->mname, NIL); + Printv(freebody, "SWIGINTERN void\n", freefunc, "(void *self) {\n", NIL); + Printv(freebody, tab4, klass->type, " *", pname0, " = (", klass->type, " *)self;\n", NIL); + Printv(freebody, tab4, NIL); + + /* Check to see if object tracking is activated for the class + that owns this destructor. */ + if (GetFlag(pn, "feature:trackobjects")) { + Printf(freebody, "SWIG_RubyRemoveTracking(%s);\n", pname0); + Printv(freebody, tab4, NIL); + } + + if (Extend) { + String *wrap = Getattr(n, "wrap:code"); + if (wrap) { + Printv(f_wrappers, wrap, NIL); + } + /* Printv(freebody, Swig_name_destroy(name), "(", pname0, ")", NIL); */ + Printv(freebody, Getattr(n, "wrap:action"), "\n", NIL); + } else { + String *action = Getattr(n, "wrap:action"); + if (action) { + Printv(freebody, action, "\n", NIL); + } else { + /* In the case swig emits no destroy function. */ + if (CPlusPlus) + Printf(freebody, "delete %s;\n", pname0); + else + Printf(freebody, "free((char*) %s);\n", pname0); + } + } + + Printv(freebody, "}\n\n", NIL); + + Printv(f_wrappers, freebody, NIL); + + klass->destructor_defined = 1; + current = NO_CPP; + Delete(freefunc); + Delete(freebody); + Delete(pname0); + return SWIG_OK; + } + + /* --------------------------------------------------------------------- + * membervariableHandler() + * + * This creates a pair of functions to set/get the variable of a member. + * -------------------------------------------------------------------- */ + + virtual int membervariableHandler(Node *n) { + String* docs = docstring(n, AUTODOC_GETTER); + Printf(f_wrappers, "%s", docs); + Delete(docs); + + if (!is_immutable(n)) { + String* docs = docstring(n, AUTODOC_SETTER); + Printf(f_wrappers, "%s", docs); + Delete(docs); + } + + current = MEMBER_VAR; + Language::membervariableHandler(n); + current = NO_CPP; + return SWIG_OK; + } + + /* ----------------------------------------------------------------------- + * staticmemberfunctionHandler() + * + * Wrap a static C++ function + * ---------------------------------------------------------------------- */ + + virtual int staticmemberfunctionHandler(Node *n) { + String* docs = docstring(n, AUTODOC_STATICFUNC); + Printf(f_wrappers, "%s", docs); + Delete(docs); + + current = STATIC_FUNC; + Language::staticmemberfunctionHandler(n); + current = NO_CPP; + return SWIG_OK; + } + + /* ---------------------------------------------------------------------- + * memberconstantHandler() + * + * Create a C++ constant + * --------------------------------------------------------------------- */ + + virtual int memberconstantHandler(Node *n) { + String* docs = docstring(n, AUTODOC_STATICFUNC); + Printf(f_wrappers, "%s", docs); + Delete(docs); + + current = CLASS_CONST; + Language::memberconstantHandler(n); + current = NO_CPP; + return SWIG_OK; + } + + /* --------------------------------------------------------------------- + * staticmembervariableHandler() + * --------------------------------------------------------------------- */ + + virtual int staticmembervariableHandler(Node *n) { + String* docs = docstring(n, AUTODOC_GETTER); + Printf(f_wrappers, "%s", docs); + Delete(docs); + + if (!is_immutable(n)) { + String* docs = docstring(n, AUTODOC_SETTER); + Printf(f_wrappers, "%s", docs); + Delete(docs); + } + + 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); + } + + /* ------------------------------------------------------------ + * 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, "SwigDirector_%s", supername); + + /* insert self parameter */ + Parm *p; + ParmList *superparms = Getattr(n, "parms"); + ParmList *parms = CopyParmList(superparms); + String *type = NewString("VALUE"); + p = NewParm(type, NewString("self"), n); + set_nextSibling(p, parms); + parms = p; + + if (!Getattr(n, "defaultargs")) { + /* constructor */ + { + Wrapper *w = NewWrapper(); + String *call; + String *basetype = Getattr(parent, "classtype"); + String *target = Swig_method_decl(0, decl, classname, parms, 0); + call = Swig_csuperclass_call(0, basetype, superparms); + Printf(w->def, "%s::%s: %s, Swig::Director(self) { }", classname, target, call); + Delete(target); + Wrapper_print(w, f_directors); + Delete(call); + DelWrapper(w); + } + + /* constructor header */ + { + String *target = Swig_method_decl(0, decl, classname, parms, 1); + Printf(f_directors_h, " %s;\n", target); + Delete(target); + } + } + + Delete(sub); + Delete(classname); + Delete(supername); + Delete(parms); + return Language::classDirectorConstructor(n); + } + + /* ------------------------------------------------------------ + * classDirectorDefaultConstructor() + * ------------------------------------------------------------ */ + + virtual int classDirectorDefaultConstructor(Node *n) { + String *classname; + Wrapper *w; + classname = Swig_class_name(n); + w = NewWrapper(); + Printf(w->def, "SwigDirector_%s::SwigDirector_%s(VALUE self) : Swig::Director(self) { }", classname, classname); + Wrapper_print(w, f_directors); + DelWrapper(w); + Printf(f_directors_h, " SwigDirector_%s(VALUE self);\n", classname); + Delete(classname); + return Language::classDirectorDefaultConstructor(n); + } + + /* --------------------------------------------------------------- + * exceptionSafeMethodCall() + * + * Emit a virtual director method to pass a method call on to the + * underlying Ruby instance. + * + * --------------------------------------------------------------- */ + + void exceptionSafeMethodCall(String *className, Node *n, Wrapper *w, int argc, String *args, bool initstack) { + Wrapper *body = NewWrapper(); + Wrapper *rescue = NewWrapper(); + + String *methodName = Getattr(n, "sym:name"); + + String *bodyName = NewStringf("%s_%s_body", className, methodName); + String *rescueName = NewStringf("%s_%s_rescue", className, methodName); + String *depthCountName = NewStringf("%s_%s_call_depth", className, methodName); + + // Check for an exception typemap of some kind + String *tm = Swig_typemap_lookup("director:except", n, Swig_cresult_name(), 0); + if (!tm) { + tm = Getattr(n, "feature:director:except"); + } + + if ((tm != 0) && (Len(tm) > 0) && (Strcmp(tm, "1") != 0)) { + // Declare a global to hold the depth count + if (!Getattr(n, "sym:nextSibling")) { + Printf(body->def, "static int %s = 0;\n", depthCountName); + + // Function body + Printf(body->def, "VALUE %s(VALUE data) {\n", bodyName); + Wrapper_add_localv(body, "args", "Swig::body_args *", "args", "= reinterpret_cast<Swig::body_args *>(data)", NIL); + Wrapper_add_localv(body, Swig_cresult_name(), "VALUE", Swig_cresult_name(), "= Qnil", NIL); + Printf(body->code, "%s++;\n", depthCountName); + Printv(body->code, Swig_cresult_name(), " = rb_funcall2(args->recv, args->id, args->argc, args->argv);\n", NIL); + Printf(body->code, "%s--;\n", depthCountName); + Printv(body->code, "return ", Swig_cresult_name(), ";\n", NIL); + Printv(body->code, "}", NIL); + + // Exception handler + Printf(rescue->def, "VALUE %s(VALUE args, VALUE error) {\n", rescueName); + Replaceall(tm, "$error", "error"); + Printf(rescue->code, "%s--;\n", depthCountName); + Printf(rescue->code, "if (%s == 0) ", depthCountName); + Printv(rescue->code, Str(tm), "\n", NIL); + Printv(rescue->code, "rb_exc_raise(error);\n", NIL); + Printv(rescue->code, "return Qnil;\n", NIL); + Printv(rescue->code, "}", NIL); + } + + // Main code + Wrapper_add_localv(w, "args", "Swig::body_args", "args", NIL); + Wrapper_add_localv(w, "status", "int", "status", NIL); + Printv(w->code, "args.recv = swig_get_self();\n", NIL); + Printf(w->code, "args.id = rb_intern(\"%s\");\n", methodName); + Printf(w->code, "args.argc = %d;\n", argc); + if (argc > 0) { + Printf(w->code, "args.argv = new VALUE[%d];\n", argc); + for (int i = 0; i < argc; i++) { + Printf(w->code, "args.argv[%d] = obj%d;\n", i, i); + } + } else { + Printv(w->code, "args.argv = 0;\n", NIL); + } + Printf(w->code, "%s = rb_protect(PROTECTFUNC(%s), reinterpret_cast<VALUE>(&args), &status);\n", Swig_cresult_name(), bodyName); + if ( initstack ) Printf(w->code, "SWIG_RELEASE_STACK;\n"); + Printf(w->code, "if (status) {\n"); + Printf(w->code, "VALUE lastErr = rb_gv_get(\"$!\");\n"); + Printf(w->code, "%s(reinterpret_cast<VALUE>(&args), lastErr);\n", rescueName); + Printf(w->code, "}\n"); + if (argc > 0) { + Printv(w->code, "delete [] args.argv;\n", NIL); + } + // Dump wrapper code + Wrapper_print(body, f_directors_helpers); + Wrapper_print(rescue, f_directors_helpers); + } else { + if (argc > 0) { + Printf(w->code, "%s = rb_funcall(swig_get_self(), rb_intern(\"%s\"), %d%s);\n", Swig_cresult_name(), methodName, argc, args); + } else { + Printf(w->code, "%s = rb_funcall(swig_get_self(), rb_intern(\"%s\"), 0, Qnil);\n", Swig_cresult_name(), methodName); + } + if ( initstack ) Printf(w->code, "SWIG_RELEASE_STACK;\n"); + } + + // Clean up + Delete(bodyName); + Delete(rescueName); + Delete(depthCountName); + DelWrapper(body); + DelWrapper(rescue); + } + + virtual int classDirectorMethod(Node *n, Node *parent, String *super) { + int is_void = 0; + int is_pointer = 0; + String *decl = Getattr(n, "decl"); + String *name = Getattr(n, "name"); + String *classname = Getattr(parent, "sym:name"); + String *c_classname = Getattr(parent, "name"); + String *symname = Getattr(n, "sym:name"); + String *declaration = NewString(""); + ParmList *l = Getattr(n, "parms"); + Wrapper *w = NewWrapper(); + String *tm; + String *wrap_args = NewString(""); + SwigType *returntype = Getattr(n, "type"); + Parm *p; + String *value = Getattr(n, "value"); + String *storage = Getattr(n, "storage"); + bool pure_virtual = false; + int status = SWIG_OK; + int idx; + bool ignored_method = GetFlag(n, "feature:ignore") ? true : false; + bool asvoid = checkAttribute( n, "feature:numoutputs", "0") ? true : false; + bool initstack = checkAttribute( n, "feature:initstack", "1") ? true : false; + + if (Cmp(storage, "virtual") == 0) { + if (Cmp(value, "0") == 0) { + pure_virtual = true; + } + } + String *overnametmp = NewString(Getattr(n, "sym:name")); + if (Getattr(n, "sym:overloaded")) { + Printf(overnametmp, "::%s", Getattr(n, "sym:overname")); + } + + /* determine if the method returns a pointer */ + is_pointer = SwigType_ispointer_return(decl); + is_void = (!Cmp(returntype, "void") && !is_pointer); + + /* virtual method definition */ + String *target; + String *pclassname = NewStringf("SwigDirector_%s", classname); + String *qualified_name = NewStringf("%s::%s", pclassname, name); + SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : Getattr(n, "classDirectorMethods:type"); + target = Swig_method_decl(rtype, decl, qualified_name, l, 0); + Printf(w->def, "%s", target); + Delete(qualified_name); + Delete(target); + /* header declaration */ + target = Swig_method_decl(rtype, decl, name, l, 1); + Printf(declaration, " virtual %s", target); + Delete(target); + + // Get any exception classes in the throws typemap + if (Getattr(n, "noexcept")) { + Append(w->def, " noexcept"); + Append(declaration, " noexcept"); + } + ParmList *throw_parm_list = 0; + + if ((throw_parm_list = Getattr(n, "throws")) || Getattr(n, "throw")) { + Parm *p; + int gencomma = 0; + + Append(w->def, " throw("); + Append(declaration, " throw("); + + if (throw_parm_list) + Swig_typemap_attach_parms("throws", throw_parm_list, 0); + for (p = throw_parm_list; p; p = nextSibling(p)) { + if (Getattr(p, "tmap:throws")) { + if (gencomma++) { + Append(w->def, ", "); + Append(declaration, ", "); + } + + Printf(w->def, "%s", SwigType_str(Getattr(p, "type"), 0)); + Printf(declaration, "%s", SwigType_str(Getattr(p, "type"), 0)); + } + } + + Append(w->def, ")"); + Append(declaration, ")"); + } + + Append(w->def, " {"); + Append(declaration, ";\n"); + + if (initstack && !(ignored_method && !pure_virtual)) { + Append(w->def, "\nSWIG_INIT_STACK;\n"); + } + + /* 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 && (!ignored_method || pure_virtual)) { + if (!SwigType_isclass(returntype)) { + if (!(SwigType_ispointer(returntype) || SwigType_isreference(returntype))) { + String *construct_result = NewStringf("= SwigValueInit< %s >()", SwigType_lstr(returntype, 0)); + Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), construct_result, NIL); + Delete(construct_result); + } else { + Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), "= 0", NIL); + } + } else { + String *cres = SwigType_lstr(returntype, "c_result"); + Printf(w->code, "%s;\n", cres); + Delete(cres); + } + } + + if (ignored_method) { + if (!pure_virtual) { + if (!is_void) + Printf(w->code, "return "); + String *super_call = Swig_method_call(super, l); + Printf(w->code, "%s;\n", super_call); + Delete(super_call); + } else { + Printf(w->code, "Swig::DirectorPureVirtualException::raise(\"Attempted to invoke pure virtual method %s::%s\");\n", SwigType_namestr(c_classname), + SwigType_namestr(name)); + } + } else { + /* attach typemaps to arguments (C/C++ -> Ruby) */ + String *arglist = NewString(""); + + Swig_director_parms_fixup(l); + + Swig_typemap_attach_parms("in", l, 0); + Swig_typemap_attach_parms("directorin", l, w); + Swig_typemap_attach_parms("directorargout", l, w); + + char source[256]; + + int outputs = 0; + if (!is_void && !asvoid) + outputs++; + + /* build argument list and type conversion string */ + idx = 0; p = l; + while ( p ) { + if (Getattr(p, "tmap:directorargout") != 0) + outputs++; + + if ( checkAttribute( p, "tmap:in:numinputs", "0") ) { + p = Getattr(p, "tmap:in:next"); + continue; + } + + String *parameterName = Getattr(p, "name"); + String *parameterType = Getattr(p, "type"); + + Putc(',', arglist); + if ((tm = Getattr(p, "tmap:directorin")) != 0) { + sprintf(source, "obj%d", idx++); + String *input = NewString(source); + Setattr(p, "emit:directorinput", input); + Replaceall(tm, "$input", input); + Replaceall(tm, "$owner", "0"); + Delete(input); + Printv(wrap_args, tm, "\n", NIL); + Wrapper_add_localv(w, source, "VALUE", source, "= Qnil", NIL); + Printv(arglist, source, NIL); + p = Getattr(p, "tmap:directorin:next"); + continue; + } else if (Cmp(parameterType, "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 "SwigDirector_$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(parameterType) || SwigType_isreference(parameterType)) { + Node *modname = Getattr(parent, "module"); + Node *target = Swig_directormap(modname, parameterType); + sprintf(source, "obj%d", idx++); + String *nonconst = 0; + /* strip pointer/reference --- should move to Swig/stype.c */ + String *nptype = NewString(Char(parameterType) + 2); + /* name as pointer */ + String *ppname = Copy(parameterName); + if (SwigType_isreference(parameterType)) { + Insert(ppname, 0, "&"); + } + /* if necessary, cast away const since Ruby doesn't support it! */ + if (SwigType_isconst(nptype)) { + nonconst = NewStringf("nc_tmp_%s", parameterName); + String *nonconst_i = NewStringf("= const_cast< %s >(%s)", SwigType_lstr(parameterType, 0), ppname); + Wrapper_add_localv(w, nonconst, SwigType_lstr(parameterType, 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(parameterType, parameterName), + SwigType_namestr(c_classname), SwigType_namestr(name)); + } else { + nonconst = Copy(ppname); + } + Delete(nptype); + Delete(ppname); + String *mangle = SwigType_manglestr(parameterType); + if (target) { + String *director = NewStringf("director_%s", mangle); + Wrapper_add_localv(w, director, "Swig::Director *", director, "= 0", NIL); + Wrapper_add_localv(w, source, "VALUE", source, "= Qnil", NIL); + Printf(wrap_args, "%s = dynamic_cast<Swig::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->swig_get_self();\n", source, director); + Printf(wrap_args, "}\n"); + 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); + } + Delete(mangle); + Delete(nonconst); + } else { + Swig_warning(WARN_TYPEMAP_DIRECTORIN_UNDEF, input_file, line_number, + "Unable to use type %s as a function argument in director method %s::%s (skipping method).\n", SwigType_str(parameterType, 0), + SwigType_namestr(c_classname), SwigType_namestr(name)); + status = SWIG_NOWRAP; + break; + } + } + p = nextSibling(p); + } + + /* declare Ruby return value */ + String *value_result = NewStringf("VALUE SWIGUNUSED %s", Swig_cresult_name()); + Wrapper_add_local(w, Swig_cresult_name(), value_result); + Delete(value_result); + + /* wrap complex arguments to VALUEs */ + Printv(w->code, wrap_args, NIL); + + /* pass the method call on to the Ruby object */ + exceptionSafeMethodCall(classname, n, w, idx, arglist, initstack); + + /* + * 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(%s) != T_ARRAY) {\n", Swig_cresult_name()); + Printf(w->code, "Ruby_DirectorTypeMismatchException(\"Ruby method failed to return an array.\");\n"); + Printf(w->code, "}\n"); + } + + idx = 0; + + /* Marshal return value */ + if (!is_void) { + tm = Swig_typemap_lookup("directorout", n, Swig_cresult_name(), w); + if (tm != 0) { + if (outputs > 1 && !asvoid ) { + Printf(w->code, "output = rb_ary_entry(%s, %d);\n", Swig_cresult_name(), idx++); + Replaceall(tm, "$input", "output"); + } else { + Replaceall(tm, "$input", Swig_cresult_name()); + } + /* 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_DIRECTOROUT_UNDEF, input_file, line_number, + "Unable to use return type %s in director method %s::%s (skipping method).\n", SwigType_str(returntype, 0), + SwigType_namestr(c_classname), SwigType_namestr(name)); + status = SWIG_ERROR; + } + } + + /* Marshal outputs */ + for (p = l; p;) { + if ((tm = Getattr(p, "tmap:directorargout")) != 0) { + if (outputs > 1) { + Printf(w->code, "output = rb_ary_entry(%s, %d);\n", Swig_cresult_name(), idx++); + Replaceall(tm, "$result", "output"); + } else { + Replaceall(tm, "$result", Swig_cresult_name()); + } + Replaceall(tm, "$input", Getattr(p, "emit:directorinput")); + Printv(w->code, tm, "\n", NIL); + p = Getattr(p, "tmap:directorargout:next"); + } else { + p = nextSibling(p); + } + } + + Delete(arglist); + Delete(cleanup); + Delete(outarg); + } + + /* any existing helper functions to handle this? */ + if (!is_void) { + if (!(ignored_method && !pure_virtual)) { + String *rettype = SwigType_str(returntype, 0); + if (!SwigType_isreference(returntype)) { + Printf(w->code, "return (%s) c_result;\n", rettype); + } else { + Printf(w->code, "return (%s) *c_result;\n", rettype); + } + Delete(rettype); + } + } + + Printf(w->code, "}\n"); + + // We expose protected methods via an extra public inline method which makes a straight call to the wrapped class' method + String *inline_extra_method = NewString(""); + if (dirprot_mode() && !is_public(n) && !pure_virtual) { + Printv(inline_extra_method, declaration, NIL); + String *extra_method_name = NewStringf("%sSwigPublic", name); + Replaceall(inline_extra_method, name, extra_method_name); + Replaceall(inline_extra_method, ";\n", " {\n "); + if (!is_void) + Printf(inline_extra_method, "return "); + String *methodcall = Swig_method_call(super, l); + Printv(inline_extra_method, methodcall, ";\n }\n", NIL); + Delete(methodcall); + Delete(extra_method_name); + } + + /* emit the director method */ + if (status == SWIG_OK) { + Replaceall(w->code, "$isvoid", is_void ? "1" : "0"); + if (!Getattr(n, "defaultargs")) { + Replaceall(w->code, "$symname", symname); + Wrapper_print(w, f_directors); + Printv(f_directors_h, declaration, NIL); + Printv(f_directors_h, inline_extra_method, NIL); + } + } + + /* clean up */ + Delete(wrap_args); + Delete(pclassname); + 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); + } + + String *runtimeCode() { + String *s = NewString(""); + String *shead = Swig_include_sys("rubyhead.swg"); + if (!shead) { + Printf(stderr, "*** Unable to open 'rubyhead.swg'\n"); + } else { + Append(s, shead); + Delete(shead); + } + String *serrors = Swig_include_sys("rubyerrors.swg"); + if (!serrors) { + Printf(stderr, "*** Unable to open 'rubyerrors.swg'\n"); + } else { + Append(s, serrors); + Delete(serrors); + } + String *strack = Swig_include_sys("rubytracking.swg"); + if (!strack) { + Printf(stderr, "*** Unable to open 'rubytracking.swg'\n"); + } else { + Append(s, strack); + Delete(strack); + } + String *sapi = Swig_include_sys("rubyapi.swg"); + if (!sapi) { + Printf(stderr, "*** Unable to open 'rubyapi.swg'\n"); + } else { + Append(s, sapi); + Delete(sapi); + } + String *srun = Swig_include_sys("rubyrun.swg"); + if (!srun) { + Printf(stderr, "*** Unable to open 'rubyrun.swg'\n"); + } else { + Append(s, srun); + Delete(srun); + } + return s; + } + + String *defaultExternalRuntimeFilename() { + return NewString("swigrubyrun.h"); + } + + /*---------------------------------------------------------------------- + * kwargsSupport() + *--------------------------------------------------------------------*/ + + bool kwargsSupport() const { + // kwargs support isn't actually implemented, but changing to return false may break something now as it turns on compactdefaultargs + return true; + } +}; /* class RUBY */ + +/* ----------------------------------------------------------------------------- + * swig_ruby() - Instantiate module + * ----------------------------------------------------------------------------- */ + +static Language *new_swig_ruby() { + return new RUBY(); +} +extern "C" Language *swig_ruby(void) { + return new_swig_ruby(); +} + + +/* + * Local Variables: + * c-basic-offset: 2 + * End: + */ |