/* -----------------------------------------------------------------------------
 * 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\
\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);
    }
    SwigType *resolved_type = SwigType_typedef_resolve_all(type);
    SwigType *unqualified_type = NIL;
    if (SwigType_isreference(resolved_type)) {
      SwigType *t = Copy(resolved_type);
      t = SwigType_del_reference(t);
      unqualified_type = SwigType_strip_qualifiers(t);
      Delete(t);
    } else {
      unqualified_type = SwigType_strip_qualifiers(resolved_type);
    }
    if (numval) {
      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(numval, "0"))
        return NewString("nil");
      return Copy(v);
    }
    if (v && Len(v) > 0) {
      if (Equal(v, "nullptr")) {
        // nullptr is type nullptr_t which doesn't implicitly convert to 0.
        Delete(resolved_type);
        Delete(unqualified_type);
        return NewString("nil");
      }
      if (Equal(v, "NULL")) {
        // The C and C++ standards both allow the implementation to define NULL
        // to `0`, and the inadvertent use of NULL as an integer zero is
        // sometimes seen in code.
        //
        // However this use is semantically wrong, and GCC and clang both
        // define NULL to a magic value which warns if implicitly converted
        // to an integer, so we only implicitly convert to zero if the type
        // resolves to a built-in arithmetic type.
        switch (SwigType_type(unqualified_type)) {
        case T_INT:
        case T_LONG:
        case T_SHORT:
        case T_UINT:
        case T_USHORT:
        case T_ULONG:
        case T_CHAR:
        case T_SCHAR:
        case T_UCHAR:
        case T_WCHAR:
        case T_FLOAT:
        case T_DOUBLE:
        case T_LONGDOUBLE:
        case T_BOOL:
        case T_LONGLONG:
        case T_ULONGLONG:
          Delete(resolved_type);
          Delete(unqualified_type);
          return NewString("0");
        default:
          Delete(resolved_type);
          Delete(unqualified_type);
          return NewString("nil");
        }
      }
    }
    Delete(resolved_type);
    Delete(unqualified_type);
    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", 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;
    }

    if (!useGlobalModule)
      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");
      Append(f->code, "try {\n");
      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, "\n((struct swig_ruby_wrapped_object *)RTYPEDDATA_GET_DATA(self))->data = %s;", result_name);
          if (GetFlag(pn, "feature:trackobjects")) {
            Printf(action, "\nSWIG_RubyAddTracking(%s, self);", result_name);
          }
          Delete(result_name);
        }
      }

      Setattr(n, "wrap:name", wname);

      /* Emit the function call */
      if (Swig_director_emit_dynamic_cast(n, f)) {
        /* Add protection */
        Append(f->code, "if (!darg) {\n");
        Append(f->code, "  Swig::DirectorMethodException::raise(rb_str_new2(\"'self' is not a director\"));\n");
        Append(f->code, "}\n");
      }
      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;
    bool check_emitted = false;
    String *tmp = NewString("");
    String *dispatch = Swig_overload_dispatch(n, "return %s(nargs, args, self);", &maxargs, &check_emitted);

    /* 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);
          }
        }
      }
    }
  }

  /* Set up the per-class TypedData descriptor: named after the C++ class (so it is
     recognisable in Ruby heap dumps and ObjectSpace statistics) with the shared mark
     and free callbacks. The flags enable embedding of the wrapper on Ruby 3.3+, the
     same as the anonymous swig_type_data_type descriptor. */
  void handleClassName(Node *) {
    Printf(klass->init, "SwigClass%s.cext_type.wrap_struct_name = \"C++ class %s\";\n", klass->name, klass->cname);
    Printf(klass->init, "SwigClass%s.cext_type.function.dmark = SWIG_Ruby_mark_swig_type;\n", klass->name);
    Printf(klass->init, "SwigClass%s.cext_type.function.dfree = SWIG_Ruby_free_swig_type;\n", klass->name);
    Printf(klass->init, "#ifdef RUBY_TYPED_FREE_IMMEDIATELY\n");
    Printf(klass->init, "SwigClass%s.cext_type.flags = SWIG_RUBY_TYPED_DATA_FLAGS;\n", klass->name);
    Printf(klass->init, "#endif\n");
  }

  /*
   * 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);
    handleClassName(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);
      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 */
    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) {
          snprintf(source, sizeof(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);
            snprintf(source, sizeof(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:
 */
