mirror of https://github.com/swig/swig
3453 lines
107 KiB
C++
3453 lines
107 KiB
C++
/* -----------------------------------------------------------------------------
|
|
* This file is part of SWIG, which is licensed as a whole under version 3
|
|
* (or any later version) of the GNU General Public License. Some additional
|
|
* terms also apply to certain portions of SWIG. The full details of the SWIG
|
|
* license and copyrights can be found in the LICENSE and COPYRIGHT files
|
|
* included with the SWIG source code as distributed by the SWIG developers
|
|
* and at https://www.swig.org/legal.html.
|
|
*
|
|
* ruby.cxx
|
|
*
|
|
* Ruby language module for SWIG.
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
#include "swigmod.h"
|
|
#include "cparse.h"
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <limits.h> /* for INT_MAX */
|
|
|
|
#define SWIG_PROTECTED_TARGET_METHODS 1
|
|
|
|
class RClass {
|
|
private:
|
|
String *temp;
|
|
|
|
public:
|
|
String *name; /* class name (renamed) */
|
|
String *cname; /* original C class/struct name */
|
|
String *mname; /* Mangled name */
|
|
|
|
/**
|
|
* The C variable name used in the SWIG-generated wrapper code to refer to
|
|
* this class; usually it is of the form "SwigClassXXX.klass", where SwigClassXXX
|
|
* is a swig_class struct instance and klass is a member of that struct.
|
|
*/
|
|
String *vname;
|
|
|
|
/**
|
|
* The C variable name used in the SWIG-generated wrapper code to refer to
|
|
* the module that implements this class's methods (when we're trying to
|
|
* support C++ multiple inheritance). Usually it is of the form
|
|
* "SwigClassClassName.mImpl", where SwigClassXXX is a swig_class struct instance
|
|
* and mImpl is a member of that struct.
|
|
*/
|
|
String *mImpl;
|
|
|
|
String *type;
|
|
String *prefix;
|
|
String *init;
|
|
|
|
|
|
int constructor_defined;
|
|
int destructor_defined;
|
|
|
|
RClass() {
|
|
temp = NewString("");
|
|
name = NewString("");
|
|
cname = NewString("");
|
|
mname = NewString("");
|
|
vname = NewString("");
|
|
mImpl = NewString("");
|
|
type = NewString("");
|
|
prefix = NewString("");
|
|
init = NewString("");
|
|
constructor_defined = 0;
|
|
destructor_defined = 0;
|
|
}
|
|
|
|
~RClass() {
|
|
Delete(name);
|
|
Delete(cname);
|
|
Delete(vname);
|
|
Delete(mImpl);
|
|
Delete(mname);
|
|
Delete(type);
|
|
Delete(prefix);
|
|
Delete(init);
|
|
Delete(temp);
|
|
}
|
|
|
|
void set_name(const_String_or_char_ptr cn, const_String_or_char_ptr rn, const_String_or_char_ptr valn) {
|
|
/* Original C/C++ class (or struct) name */
|
|
Clear(cname);
|
|
Append(cname, cn);
|
|
|
|
/* Mangled name */
|
|
Delete(mname);
|
|
mname = Swig_name_mangle_string(cname);
|
|
|
|
/* Renamed class name */
|
|
Clear(name);
|
|
Append(name, valn);
|
|
|
|
/* Variable name for the VALUE that refers to the Ruby Class object */
|
|
Clear(vname);
|
|
Printf(vname, "SwigClass%s.klass", name);
|
|
|
|
/* Variable name for the VALUE that refers to the Ruby Class object */
|
|
Clear(mImpl);
|
|
Printf(mImpl, "SwigClass%s.mImpl", name);
|
|
|
|
/* Prefix */
|
|
Clear(prefix);
|
|
Printv(prefix, (rn ? rn : cn), "_", NIL);
|
|
}
|
|
|
|
char *strip(const_String_or_char_ptr s) {
|
|
Clear(temp);
|
|
if (Strncmp(s, prefix, Len(prefix)) == 0) {
|
|
Append(temp, Char(s) + Len(prefix));
|
|
} else {
|
|
Append(temp, s);
|
|
}
|
|
return Char(temp);
|
|
}
|
|
};
|
|
|
|
|
|
/* flags for the make_autodoc function */
|
|
namespace {
|
|
enum autodoc_t {
|
|
AUTODOC_CLASS,
|
|
AUTODOC_CTOR,
|
|
AUTODOC_DTOR,
|
|
AUTODOC_STATICFUNC,
|
|
AUTODOC_FUNC,
|
|
AUTODOC_METHOD,
|
|
AUTODOC_GETTER,
|
|
AUTODOC_SETTER,
|
|
AUTODOC_NONE
|
|
};
|
|
}
|
|
|
|
static const char *usage = "\
|
|
Ruby Options (available with -ruby)\n\
|
|
-autorename - Enable renaming of classes and methods to follow Ruby coding standards\n\
|
|
-globalmodule - Wrap everything into the global module\n\
|
|
-initname <name>- Set entry function to Init_<name> (used by `require')\n\
|
|
-minherit - Attempt to support multiple inheritance\n\
|
|
-noautorename - Disable renaming of classes and methods (default)\n\
|
|
-prefix <name> - Set a prefix <name> to be prepended to all names\n\
|
|
";
|
|
|
|
|
|
#define RCLASS(hash, name) (RClass*)(Getattr(hash, name) ? Data(Getattr(hash, name)) : 0)
|
|
#define SET_RCLASS(hash, name, klass) Setattr(hash, name, NewVoid(klass, 0))
|
|
|
|
|
|
class RUBY:public Language {
|
|
private:
|
|
|
|
String *module;
|
|
String *modvar;
|
|
String *feature;
|
|
String *prefix;
|
|
int current;
|
|
Hash *classes; /* key=cname val=RClass */
|
|
RClass *klass; /* Currently processing class */
|
|
Hash *special_methods; /* Python style special method name table */
|
|
|
|
File *f_directors;
|
|
File *f_directors_h;
|
|
File *f_directors_helpers;
|
|
File *f_begin;
|
|
File *f_runtime;
|
|
File *f_runtime_h;
|
|
File *f_header;
|
|
File *f_wrappers;
|
|
File *f_init;
|
|
File *f_initbeforefunc;
|
|
|
|
bool useGlobalModule;
|
|
bool multipleInheritance;
|
|
|
|
// Wrap modes
|
|
enum WrapperMode {
|
|
NO_CPP,
|
|
MEMBER_FUNC,
|
|
CONSTRUCTOR_ALLOCATE,
|
|
CONSTRUCTOR_INITIALIZE,
|
|
DESTRUCTOR,
|
|
MEMBER_VAR,
|
|
CLASS_CONST,
|
|
STATIC_FUNC,
|
|
STATIC_VAR
|
|
};
|
|
|
|
/* ------------------------------------------------------------
|
|
* autodoc level declarations
|
|
* ------------------------------------------------------------ */
|
|
|
|
enum autodoc_l {
|
|
NO_AUTODOC = -2, // no autodoc
|
|
STRING_AUTODOC = -1, // use provided string
|
|
NAMES_AUTODOC = 0, // only parameter names
|
|
TYPES_AUTODOC = 1, // parameter names and types
|
|
EXTEND_AUTODOC = 2, // extended documentation and parameter names
|
|
EXTEND_TYPES_AUTODOC = 3 // extended documentation and parameter types + names
|
|
};
|
|
|
|
autodoc_t last_mode;
|
|
String* last_autodoc;
|
|
|
|
autodoc_l autodoc_level(String *autodoc) {
|
|
autodoc_l dlevel = NO_AUTODOC;
|
|
char *c = Char(autodoc);
|
|
if (c) {
|
|
if (isdigit(c[0])) {
|
|
dlevel = (autodoc_l) atoi(c);
|
|
} else {
|
|
if (strcmp(c, "extended") == 0) {
|
|
dlevel = EXTEND_AUTODOC;
|
|
} else {
|
|
dlevel = STRING_AUTODOC;
|
|
}
|
|
}
|
|
}
|
|
return dlevel;
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------
|
|
* have_docstring()
|
|
* Check if there is a docstring directive and it has text,
|
|
* or there is an autodoc flag set
|
|
* ------------------------------------------------------------ */
|
|
|
|
bool have_docstring(Node *n) {
|
|
String *str = Getattr(n, "feature:docstring");
|
|
return (str && Len(str) > 0) || (Getattr(n, "feature:autodoc") && !GetFlag(n, "feature:noautodoc"));
|
|
}
|
|
|
|
/* ------------------------------------------------------------
|
|
* docstring()
|
|
* Get the docstring text, stripping off {} if necessary,
|
|
* and enclose in triple double quotes. If autodoc is also
|
|
* set then it will build a combined docstring.
|
|
* ------------------------------------------------------------ */
|
|
|
|
String *docstring(Node *n, autodoc_t ad_type) {
|
|
|
|
String *str = Getattr(n, "feature:docstring");
|
|
bool have_ds = (str && Len(str) > 0);
|
|
bool have_auto = (Getattr(n, "feature:autodoc") && !GetFlag(n, "feature:noautodoc"));
|
|
String *autodoc = NULL;
|
|
String *doc = NULL;
|
|
|
|
if (have_ds) {
|
|
char *t = Char(str);
|
|
if (*t == '{') {
|
|
Delitem(str, 0);
|
|
Delitem(str, DOH_END);
|
|
}
|
|
}
|
|
|
|
if (have_auto) {
|
|
autodoc = make_autodoc(n, ad_type);
|
|
have_auto = (autodoc && Len(autodoc) > 0);
|
|
}
|
|
|
|
if (have_auto || have_ds)
|
|
doc = NewString("/*");
|
|
|
|
if (have_auto && have_ds) { // Both autodoc and docstring are present
|
|
Printv(doc, "\n", autodoc, "\n", str, "\n", NIL);
|
|
} else if (!have_auto && have_ds) { // only docstring
|
|
Printv(doc, str, NIL);
|
|
} else if (have_auto && !have_ds) { // only autodoc
|
|
Printv(doc, "\n", autodoc, "\n", NIL);
|
|
} else {
|
|
doc = NewString("");
|
|
}
|
|
|
|
if (have_auto || have_ds)
|
|
Append(doc, "*/\n");
|
|
|
|
// Save the generated strings in the parse tree in case they are used later
|
|
// by post processing tools
|
|
Setattr(n, "ruby:docstring", doc);
|
|
Setattr(n, "ruby:autodoc", autodoc);
|
|
return doc;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* addMissingParameterNames()
|
|
* For functions that have not had nameless parameters set in the Language class.
|
|
*
|
|
* Inputs:
|
|
* plist - entire parameter list
|
|
* arg_offset - argument number for first parameter
|
|
* Side effects:
|
|
* The "lname" attribute in each parameter in plist will be contain a parameter name
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
void addMissingParameterNames(Node* n, ParmList *plist, int arg_offset) {
|
|
Parm *p = plist;
|
|
int i = arg_offset;
|
|
while (p) {
|
|
if (!Getattr(p, "lname")) {
|
|
String *name = makeParameterName(n, p, i);
|
|
Setattr(p, "lname", name);
|
|
Delete(name);
|
|
}
|
|
i++;
|
|
p = nextSibling(p);
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------
|
|
* make_autodocParmList()
|
|
* Generate the documentation for the function parameters
|
|
* ------------------------------------------------------------ */
|
|
|
|
String *make_autodocParmList(Node *n, bool showTypes) {
|
|
String *doc = NewString("");
|
|
String *pdocs = 0;
|
|
ParmList *plist = CopyParmList(Getattr(n, "parms"));
|
|
Parm *p;
|
|
Parm *pnext;
|
|
int lines = 0;
|
|
int arg_num = is_wrapping_class() ? 1 : 0;
|
|
const int maxwidth = 80;
|
|
|
|
addMissingParameterNames(n, plist, arg_num); // for $1_name substitutions done in Swig_typemap_attach_parms
|
|
|
|
Swig_typemap_attach_parms("in", plist, 0);
|
|
Swig_typemap_attach_parms("doc", plist, 0);
|
|
|
|
if (Strcmp(ParmList_protostr(plist), "void") == 0) {
|
|
//No parameters actually
|
|
return doc;
|
|
}
|
|
|
|
for (p = plist; p; p = pnext, arg_num++) {
|
|
|
|
String *tm = Getattr(p, "tmap:in");
|
|
if (tm) {
|
|
pnext = Getattr(p, "tmap:in:next");
|
|
if (checkAttribute(p, "tmap:in:numinputs", "0")) {
|
|
continue;
|
|
}
|
|
} else {
|
|
pnext = nextSibling(p);
|
|
}
|
|
|
|
String *name = 0;
|
|
String *type = 0;
|
|
String *value = 0;
|
|
String *pdoc = Getattr(p, "tmap:doc");
|
|
if (pdoc) {
|
|
name = Getattr(p, "tmap:doc:name");
|
|
type = Getattr(p, "tmap:doc:type");
|
|
value = Getattr(p, "tmap:doc:value");
|
|
}
|
|
|
|
// Note: the generated name should be consistent with that in kwnames[]
|
|
String *made_name = 0;
|
|
if (!name) {
|
|
name = made_name = makeParameterName(n, p, arg_num);
|
|
}
|
|
|
|
type = type ? type : Getattr(p, "type");
|
|
value = value ? value : Getattr(p, "value");
|
|
|
|
if (SwigType_isvarargs(type))
|
|
break;
|
|
|
|
// Skip the 'self' parameter which in ruby is implicit
|
|
if ( Cmp(name, "self") == 0 )
|
|
continue;
|
|
|
|
// Make __p parameters just p (as used in STL)
|
|
Replace( name, "__", "", DOH_REPLACE_FIRST );
|
|
|
|
if (Len(doc)) {
|
|
// add a comma to the previous one if any
|
|
Append(doc, ", ");
|
|
|
|
// Do we need to wrap a long line?
|
|
if ((Len(doc) - lines * maxwidth) > maxwidth) {
|
|
Printf(doc, "\n%s", tab4);
|
|
lines += 1;
|
|
}
|
|
}
|
|
|
|
// Do the param type too?
|
|
Node *nn = classLookup(Getattr(p, "type"));
|
|
String *type_str = nn ? Copy(Getattr(nn, "sym:name")) : SwigType_str(type, 0);
|
|
if (showTypes)
|
|
Printf(doc, "%s ", type_str);
|
|
|
|
Append(doc, name);
|
|
if (pdoc) {
|
|
if (!pdocs)
|
|
pdocs = NewString("Parameters:\n");
|
|
Printf(pdocs, " %s.\n", pdoc);
|
|
}
|
|
|
|
if (value) {
|
|
String *new_value = convertValue(value, Getattr(p, "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);
|
|
}
|
|
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, SwigType *t) {
|
|
if (v && Len(v) > 0) {
|
|
char fc = (Char(v))[0];
|
|
if (('0' <= fc && fc <= '9') || '\'' == fc || '"' == fc) {
|
|
/* number or string (or maybe NULL pointer) */
|
|
if (SwigType_ispointer(t) && Strcmp(v, "0") == 0)
|
|
return NewString("None");
|
|
else
|
|
return v;
|
|
}
|
|
if (Strcmp(v, "NULL") == 0 || Strcmp(v, "nullptr") == 0)
|
|
return SwigType_ispointer(t) ? NewString("nil") : NewString("0");
|
|
if (Strcmp(v, "true") == 0 || Strcmp(v, "TRUE") == 0)
|
|
return NewString("True");
|
|
if (Strcmp(v, "false") == 0 || Strcmp(v, "FALSE") == 0)
|
|
return NewString("False");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public:
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* RUBY()
|
|
*
|
|
* Initialize member data
|
|
* --------------------------------------------------------------------- */
|
|
RUBY() :
|
|
module(0),
|
|
modvar(0),
|
|
feature(0),
|
|
prefix(0),
|
|
current(0),
|
|
classes(0),
|
|
klass(0),
|
|
special_methods(0),
|
|
f_directors(0),
|
|
f_directors_h(0),
|
|
f_directors_helpers(0),
|
|
f_begin(0),
|
|
f_runtime(0),
|
|
f_runtime_h(0),
|
|
f_header(0),
|
|
f_wrappers(0),
|
|
f_init(0),
|
|
f_initbeforefunc(0),
|
|
useGlobalModule(false),
|
|
multipleInheritance(false),
|
|
last_mode(AUTODOC_NONE),
|
|
last_autodoc(NewString("")) {
|
|
current = NO_CPP;
|
|
director_prot_ctor_code = NewString("");
|
|
Printv(director_prot_ctor_code,
|
|
"if ( $comparison ) { /* subclassed */\n",
|
|
" $director_new \n",
|
|
"} else {\n", " rb_raise(rb_eRuntimeError,\"accessing abstract class or protected constructor\"); \n", " return Qnil;\n", "}\n", NIL);
|
|
director_multiple_inheritance = 0;
|
|
directorLanguage();
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* main()
|
|
*
|
|
* Parse command line options and initializes variables.
|
|
* --------------------------------------------------------------------- */
|
|
|
|
virtual void main(int argc, char *argv[]) {
|
|
|
|
int autorename = 0;
|
|
|
|
/* Set location of SWIG library */
|
|
SWIG_library_directory("ruby");
|
|
|
|
/* Look for certain command line options */
|
|
for (int i = 1; i < argc; i++) {
|
|
if (argv[i]) {
|
|
if (strcmp(argv[i], "-initname") == 0) {
|
|
if (argv[i + 1]) {
|
|
char *name = argv[i + 1];
|
|
feature = NewString(name);
|
|
Swig_mark_arg(i);
|
|
Swig_mark_arg(i + 1);
|
|
i++;
|
|
} else {
|
|
Swig_arg_error();
|
|
}
|
|
} else if (strcmp(argv[i], "-globalmodule") == 0) {
|
|
useGlobalModule = true;
|
|
Swig_mark_arg(i);
|
|
} else if (strcmp(argv[i], "-minherit") == 0) {
|
|
multipleInheritance = true;
|
|
director_multiple_inheritance = 1;
|
|
Swig_mark_arg(i);
|
|
} else if (strcmp(argv[i], "-autorename") == 0) {
|
|
autorename = 1;
|
|
Swig_mark_arg(i);
|
|
} else if (strcmp(argv[i], "-noautorename") == 0) {
|
|
autorename = 0;
|
|
Swig_mark_arg(i);
|
|
} else if (strcmp(argv[i], "-prefix") == 0) {
|
|
if (argv[i + 1]) {
|
|
char *name = argv[i + 1];
|
|
prefix = NewString(name);
|
|
Swig_mark_arg(i);
|
|
Swig_mark_arg(i + 1);
|
|
i++;
|
|
} else {
|
|
Swig_arg_error();
|
|
}
|
|
} else if (strcmp(argv[i], "-help") == 0) {
|
|
Printf(stdout, "%s\n", usage);
|
|
} else if (strcmp(argv[i], "-cppcast") == 0) {
|
|
Printf(stderr, "Deprecated command line option: %s. This option is now always on.\n", argv[i]);
|
|
Swig_mark_arg(i);
|
|
} else if (strcmp(argv[i], "-nocppcast") == 0) {
|
|
Printf(stderr, "Deprecated command line option: %s. This option is no longer supported.\n", argv[i]);
|
|
Swig_mark_arg(i);
|
|
Exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (autorename) {
|
|
/* Turn on the autorename mode */
|
|
Preprocessor_define((DOH *) "SWIG_RUBY_AUTORENAME", 0);
|
|
}
|
|
|
|
/* Add a symbol to the parser for conditional compilation */
|
|
Preprocessor_define("SWIGRUBY 1", 0);
|
|
|
|
/* Add typemap definitions */
|
|
SWIG_typemap_lang("ruby");
|
|
SWIG_config_file("ruby.swg");
|
|
allow_overloading();
|
|
}
|
|
|
|
/**
|
|
* Generate initialization code to define the Ruby module(s),
|
|
* accounting for nested modules as necessary.
|
|
*/
|
|
void defineRubyModule() {
|
|
List *modules = Split(module, ':', INT_MAX);
|
|
if (modules != 0 && Len(modules) > 0) {
|
|
String *mv = 0;
|
|
Iterator m;
|
|
m = First(modules);
|
|
while (m.item) {
|
|
if (Len(m.item) > 0) {
|
|
if (mv != 0) {
|
|
Printv(f_init, tab4, modvar, " = rb_define_module_under(", modvar, ", \"", m.item, "\");\n", NIL);
|
|
} else {
|
|
Printv(f_init, tab4, modvar, " = rb_define_module(\"", m.item, "\");\n", NIL);
|
|
mv = NewString(modvar);
|
|
}
|
|
}
|
|
m = Next(m);
|
|
}
|
|
Delete(mv);
|
|
Delete(modules);
|
|
}
|
|
}
|
|
|
|
void registerMagicMethods() {
|
|
|
|
special_methods = NewHash();
|
|
|
|
/* Python->Ruby style special method name. */
|
|
/* Basic */
|
|
Setattr(special_methods, "__repr__", "inspect");
|
|
Setattr(special_methods, "__str__", "to_s");
|
|
Setattr(special_methods, "__cmp__", "<=>");
|
|
Setattr(special_methods, "__hash__", "hash");
|
|
Setattr(special_methods, "__nonzero__", "nonzero?");
|
|
|
|
/* Callable */
|
|
Setattr(special_methods, "__call__", "call");
|
|
|
|
/* Collection */
|
|
Setattr(special_methods, "__len__", "length");
|
|
Setattr(special_methods, "__getitem__", "[]");
|
|
Setattr(special_methods, "__setitem__", "[]=");
|
|
|
|
/* Operators */
|
|
Setattr(special_methods, "__add__", "+");
|
|
Setattr(special_methods, "__pos__", "+@");
|
|
Setattr(special_methods, "__sub__", "-");
|
|
Setattr(special_methods, "__neg__", "-@");
|
|
Setattr(special_methods, "__mul__", "*");
|
|
Setattr(special_methods, "__div__", "/");
|
|
Setattr(special_methods, "__mod__", "%");
|
|
Setattr(special_methods, "__lshift__", "<<");
|
|
Setattr(special_methods, "__rshift__", ">>");
|
|
Setattr(special_methods, "__and__", "&");
|
|
Setattr(special_methods, "__or__", "|");
|
|
Setattr(special_methods, "__xor__", "^");
|
|
Setattr(special_methods, "__invert__", "~");
|
|
Setattr(special_methods, "__lt__", "<");
|
|
Setattr(special_methods, "__le__", "<=");
|
|
Setattr(special_methods, "__gt__", ">");
|
|
Setattr(special_methods, "__ge__", ">=");
|
|
Setattr(special_methods, "__eq__", "==");
|
|
|
|
/* Other numeric */
|
|
Setattr(special_methods, "__divmod__", "divmod");
|
|
Setattr(special_methods, "__pow__", "**");
|
|
Setattr(special_methods, "__abs__", "abs");
|
|
Setattr(special_methods, "__int__", "to_i");
|
|
Setattr(special_methods, "__float__", "to_f");
|
|
Setattr(special_methods, "__coerce__", "coerce");
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* top()
|
|
* --------------------------------------------------------------------- */
|
|
|
|
virtual int top(Node *n) {
|
|
|
|
String *mod_docstring = NULL;
|
|
|
|
/**
|
|
* See if any Ruby module options have been specified as options
|
|
* to the %module directive.
|
|
*/
|
|
Node *swigModule = Getattr(n, "module");
|
|
if (swigModule) {
|
|
Node *options = Getattr(swigModule, "options");
|
|
if (options) {
|
|
if (Getattr(options, "directors")) {
|
|
allow_directors();
|
|
}
|
|
if (Getattr(options, "dirprot")) {
|
|
allow_dirprot();
|
|
}
|
|
if (Getattr(options, "ruby_globalmodule")) {
|
|
useGlobalModule = true;
|
|
}
|
|
if (Getattr(options, "ruby_minherit")) {
|
|
multipleInheritance = true;
|
|
director_multiple_inheritance = 1;
|
|
}
|
|
mod_docstring = Getattr(options, "docstring");
|
|
}
|
|
}
|
|
|
|
/* Set comparison with none for ConstructorToFunction */
|
|
|
|
|
|
setSubclassInstanceCheck(NewStringf("strcmp(rb_obj_classname(self), classname) != 0"));
|
|
// setSubclassInstanceCheck(NewString("CLASS_OF(self) != cFoo.klass"));
|
|
|
|
/* Initialize all of the output files */
|
|
String *outfile = Getattr(n, "outfile");
|
|
String *outfile_h = Getattr(n, "outfile_h");
|
|
|
|
if (!outfile) {
|
|
Printf(stderr, "Unable to determine outfile\n");
|
|
Exit(EXIT_FAILURE);
|
|
}
|
|
|
|
f_begin = NewFile(outfile, "w", SWIG_output_files());
|
|
if (!f_begin) {
|
|
FileErrorDisplay(outfile);
|
|
Exit(EXIT_FAILURE);
|
|
}
|
|
|
|
f_runtime = NewString("");
|
|
f_init = NewString("");
|
|
f_header = NewString("");
|
|
f_wrappers = NewString("");
|
|
f_directors_h = NewString("");
|
|
f_directors = NewString("");
|
|
f_directors_helpers = NewString("");
|
|
f_initbeforefunc = NewString("");
|
|
|
|
if (Swig_directors_enabled()) {
|
|
if (!outfile_h) {
|
|
Printf(stderr, "Unable to determine outfile_h\n");
|
|
Exit(EXIT_FAILURE);
|
|
}
|
|
f_runtime_h = NewFile(outfile_h, "w", SWIG_output_files());
|
|
if (!f_runtime_h) {
|
|
FileErrorDisplay(outfile_h);
|
|
Exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* Register file targets with the SWIG file handler */
|
|
Swig_register_filebyname("header", f_header);
|
|
Swig_register_filebyname("wrapper", f_wrappers);
|
|
Swig_register_filebyname("begin", f_begin);
|
|
Swig_register_filebyname("runtime", f_runtime);
|
|
Swig_register_filebyname("init", f_init);
|
|
Swig_register_filebyname("director", f_directors);
|
|
Swig_register_filebyname("director_h", f_directors_h);
|
|
Swig_register_filebyname("director_helpers", f_directors_helpers);
|
|
Swig_register_filebyname("initbeforefunc", f_initbeforefunc);
|
|
|
|
modvar = 0;
|
|
current = NO_CPP;
|
|
klass = 0;
|
|
classes = NewHash();
|
|
|
|
registerMagicMethods();
|
|
|
|
Swig_banner(f_begin);
|
|
|
|
Swig_obligatory_macros(f_runtime, "RUBY");
|
|
|
|
if (Swig_directors_enabled()) {
|
|
Printf(f_runtime, "#define SWIG_DIRECTORS\n");
|
|
}
|
|
|
|
Printf(f_runtime, "\n");
|
|
|
|
/* typedef void *VALUE */
|
|
SwigType *value = NewSwigType(T_VOID);
|
|
SwigType_add_pointer(value);
|
|
SwigType_typedef(value, "VALUE");
|
|
Delete(value);
|
|
|
|
/* Set module name */
|
|
set_module(Char(Getattr(n, "name")));
|
|
|
|
if (Swig_directors_enabled()) {
|
|
/* Build a version of the module name for use in a C macro name. */
|
|
String *module_macro = Copy(module);
|
|
Replaceall(module_macro, "::", "__");
|
|
|
|
Swig_banner(f_directors_h);
|
|
Printf(f_directors_h, "\n");
|
|
Printf(f_directors_h, "#ifndef SWIG_%s_WRAP_H_\n", module_macro);
|
|
Printf(f_directors_h, "#define SWIG_%s_WRAP_H_\n\n", module_macro);
|
|
Printf(f_directors_h, "namespace Swig {\n");
|
|
Printf(f_directors_h, " class Director;\n");
|
|
Printf(f_directors_h, "}\n\n");
|
|
|
|
Printf(f_directors_helpers, "/* ---------------------------------------------------\n");
|
|
Printf(f_directors_helpers, " * C++ director class helpers\n");
|
|
Printf(f_directors_helpers, " * --------------------------------------------------- */\n\n");
|
|
|
|
Printf(f_directors, "\n\n");
|
|
Printf(f_directors, "/* ---------------------------------------------------\n");
|
|
Printf(f_directors, " * C++ director class methods\n");
|
|
Printf(f_directors, " * --------------------------------------------------- */\n\n");
|
|
if (outfile_h) {
|
|
String *filename = Swig_file_filename(outfile_h);
|
|
Printf(f_directors, "#include \"%s\"\n\n", filename);
|
|
Delete(filename);
|
|
}
|
|
|
|
Delete(module_macro);
|
|
}
|
|
|
|
Printf(f_header, "#define SWIG_init Init_%s\n", feature);
|
|
Printf(f_header, "#define SWIG_name \"%s\"\n\n", module);
|
|
|
|
if (mod_docstring) {
|
|
if (Len(mod_docstring)) {
|
|
Printf(f_header, "/*\n Document-module: %s\n\n%s\n*/\n", module, mod_docstring);
|
|
}
|
|
Delete(mod_docstring);
|
|
mod_docstring = NULL;
|
|
}
|
|
|
|
Printf(f_header, "static VALUE %s;\n", modvar);
|
|
|
|
/* Start generating the initialization function */
|
|
String* docs = docstring(n, AUTODOC_CLASS);
|
|
Printf(f_init, "/*\n%s\n*/", docs );
|
|
Printv(f_init, "\n", "#ifdef __cplusplus\n", "extern \"C\"\n", "#endif\n", "SWIGEXPORT void Init_", feature, "(void) {\n", "size_t i;\n", "\n", NIL);
|
|
|
|
Printv(f_init, tab4, "SWIG_InitRuntime();\n", NIL);
|
|
|
|
if (!useGlobalModule)
|
|
defineRubyModule();
|
|
|
|
Printv(f_init, "\n", "SWIG_InitializeModule(0);\n", "for (i = 0; i < swig_module.size; i++) {\n", "SWIG_define_class(swig_module.types[i]);\n", "}\n", NIL);
|
|
Printf(f_init, "\n");
|
|
|
|
/* Initialize code to keep track of objects */
|
|
Printf(f_init, "SWIG_RubyInitializeTrackings();\n");
|
|
|
|
Language::top(n);
|
|
|
|
if (Swig_directors_enabled()) {
|
|
// Insert director runtime into the f_runtime file (make it occur before %header section)
|
|
Swig_insert_file("director_common.swg", f_runtime);
|
|
Swig_insert_file("director.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 *t = Getattr(n, "type");
|
|
ParmList *l = Getattr(n, "parms");
|
|
int director_method = 0;
|
|
String *tm;
|
|
|
|
int need_result = 0;
|
|
|
|
/* Ruby needs no destructor wrapper */
|
|
if (current == DESTRUCTOR)
|
|
return SWIG_NOWRAP;
|
|
|
|
nodeType = Getattr(n, "nodeType");
|
|
destructor = (!Cmp(nodeType, "destructor"));
|
|
|
|
/* If the C++ class constructor is overloaded, we only want to
|
|
* write out the "new" singleton method once since it is always
|
|
* the same. (It's the "initialize" method that will handle the
|
|
* overloading). */
|
|
|
|
if (current == CONSTRUCTOR_ALLOCATE && Swig_symbol_isoverloaded(n) && Getattr(n, "sym:nextSibling") != 0)
|
|
return SWIG_OK;
|
|
|
|
String *overname = 0;
|
|
if (Getattr(n, "sym:overloaded")) {
|
|
overname = Getattr(n, "sym:overname");
|
|
} else {
|
|
if (!addSymbol(symname, n))
|
|
return SWIG_ERROR;
|
|
}
|
|
|
|
String *cleanup = NewString("");
|
|
String *outarg = NewString("");
|
|
String *kwargs = NewString("");
|
|
Wrapper *f = NewWrapper();
|
|
|
|
/* Rename predicate methods */
|
|
if (GetFlag(n, "feature:predicate")) {
|
|
Append(symname, "?");
|
|
}
|
|
|
|
/* Rename bang methods */
|
|
if (GetFlag(n, "feature:bang")) {
|
|
Append(symname, "!");
|
|
}
|
|
|
|
/* Determine the name of the SWIG wrapper function */
|
|
String *wname = Swig_name_wrapper(symname);
|
|
if (overname && current != CONSTRUCTOR_ALLOCATE) {
|
|
Append(wname, overname);
|
|
}
|
|
|
|
/* Emit arguments */
|
|
if (current != CONSTRUCTOR_ALLOCATE) {
|
|
emit_parameter_variables(l, f);
|
|
}
|
|
|
|
/* Attach standard typemaps */
|
|
if (current != CONSTRUCTOR_ALLOCATE) {
|
|
emit_attach_parmmaps(l, f);
|
|
}
|
|
Setattr(n, "wrap:parms", l);
|
|
|
|
/* Get number of arguments */
|
|
int numarg = emit_num_arguments(l);
|
|
int numreq = emit_num_required(l);
|
|
int varargs = emit_isvarargs(l);
|
|
bool allow_kwargs = GetFlag(n, "feature:kwargs") ? true : false;
|
|
|
|
bool ctor_director = (current == CONSTRUCTOR_INITIALIZE && Swig_directorclass(n));
|
|
int start = (current == MEMBER_FUNC || current == MEMBER_VAR || ctor_director) ? 1 : 0;
|
|
|
|
/* Now write the wrapper function itself */
|
|
if (current == CONSTRUCTOR_ALLOCATE) {
|
|
Printv(f->def, "SWIGINTERN VALUE\n", NIL);
|
|
Printf(f->def, "#ifdef HAVE_RB_DEFINE_ALLOC_FUNC\n");
|
|
Printv(f->def, wname, "(VALUE self)\n", NIL);
|
|
Printf(f->def, "#else\n");
|
|
Printv(f->def, wname, "(int argc, VALUE *argv, VALUE self)\n", NIL);
|
|
Printf(f->def, "#endif\n");
|
|
Printv(f->def, "{\n", NIL);
|
|
} else if (current == CONSTRUCTOR_INITIALIZE) {
|
|
Printv(f->def, "SWIGINTERN VALUE\n", wname, "(int argc, VALUE *argv, VALUE self) {", NIL);
|
|
if (!varargs) {
|
|
Printf(f->code, "if ((argc < %d) || (argc > %d)) ", numreq - start, numarg - start);
|
|
} else {
|
|
Printf(f->code, "if (argc < %d) ", numreq - start);
|
|
}
|
|
Printf(f->code, "{rb_raise(rb_eArgError, \"wrong # of arguments(%%d for %d)\",argc); SWIG_fail;}\n", numreq - start);
|
|
} else {
|
|
|
|
if ( current == NO_CPP )
|
|
{
|
|
String* docs = docstring(n, AUTODOC_FUNC);
|
|
Printf(f_wrappers, "%s", docs);
|
|
Delete(docs);
|
|
}
|
|
|
|
Printv(f->def, "SWIGINTERN VALUE\n", wname, "(int argc, VALUE *argv, VALUE self) {", NIL);
|
|
if (!varargs) {
|
|
Printf(f->code, "if ((argc < %d) || (argc > %d)) ", numreq - start, numarg - start);
|
|
} else {
|
|
Printf(f->code, "if (argc < %d) ", numreq - start);
|
|
}
|
|
Printf(f->code, "{rb_raise(rb_eArgError, \"wrong # of arguments(%%d for %d)\",argc); SWIG_fail;}\n", numreq - start);
|
|
}
|
|
|
|
/* Now walk the function parameter list and generate code */
|
|
/* to get arguments */
|
|
if (current != CONSTRUCTOR_ALLOCATE) {
|
|
marshalInputArgs(n, l, numarg, numreq, kwargs, allow_kwargs, f);
|
|
}
|
|
// FIXME?
|
|
if (ctor_director) {
|
|
numarg--;
|
|
numreq--;
|
|
}
|
|
|
|
/* Insert constraint checking code */
|
|
insertConstraintCheckingCode(l, f);
|
|
|
|
/* Insert cleanup code */
|
|
insertCleanupCode(l, cleanup);
|
|
|
|
/* Insert argument output code */
|
|
insertArgOutputCode(l, outarg, need_result);
|
|
|
|
/* if the object is a director, and the method call originated from its
|
|
* underlying Ruby object, resolve the call by going up the c++
|
|
* inheritance chain. otherwise try to resolve the method in Ruby.
|
|
* without this check an infinite loop is set up between the director and
|
|
* shadow class method calls.
|
|
*/
|
|
|
|
// NOTE: this code should only be inserted if this class is the
|
|
// base class of a director class. however, in general we haven't
|
|
// yet analyzed all classes derived from this one to see if they are
|
|
// directors. furthermore, this class may be used as the base of
|
|
// a director class defined in a completely different module at a
|
|
// later time, so this test must be included whether or not directorbase
|
|
// is true. we do skip this code if directors have not been enabled
|
|
// at the command line to preserve source-level compatibility with
|
|
// non-polymorphic swig. also, if this wrapper is for a smart-pointer
|
|
// method, there is no need to perform the test since the calling object
|
|
// (the smart-pointer) and the director object (the "pointee") are
|
|
// distinct.
|
|
|
|
director_method = is_member_director(n) && !is_smart_pointer() && !destructor;
|
|
if (director_method) {
|
|
Wrapper_add_local(f, "director", "Swig::Director *director = 0");
|
|
Printf(f->code, "director = dynamic_cast<Swig::Director *>(arg1);\n");
|
|
Wrapper_add_local(f, "upcall", "bool upcall = false");
|
|
Append(f->code, "upcall = (director && (director->swig_get_self() == self));\n");
|
|
}
|
|
|
|
/* Now write code to make the function call */
|
|
if (current != CONSTRUCTOR_ALLOCATE) {
|
|
if (current == CONSTRUCTOR_INITIALIZE) {
|
|
Node *pn = Swig_methodclass(n);
|
|
String *symname = Getattr(pn, "sym:name");
|
|
String *action = Getattr(n, "wrap:action");
|
|
if (Swig_directors_enabled()) {
|
|
String *classname = NewStringf("const char *classname SWIGUNUSED = \"%s::%s\"", module, symname);
|
|
Wrapper_add_local(f, "classname", classname);
|
|
}
|
|
if (action) {
|
|
SwigType *smart = Getattr(pn, "smart");
|
|
String *result_name = NewStringf("%s%s", smart ? "smart" : "", Swig_cresult_name());
|
|
if (smart) {
|
|
String *result_var = NewStringf("%s *%s = 0", SwigType_namestr(smart), result_name);
|
|
Wrapper_add_local(f, result_name, result_var);
|
|
Printf(action, "\n%s = new %s(%s);", result_name, SwigType_namestr(smart), Swig_cresult_name());
|
|
}
|
|
Printf(action, "\nDATA_PTR(self) = %s;", result_name);
|
|
if (GetFlag(pn, "feature:trackobjects")) {
|
|
Printf(action, "\nSWIG_RubyAddTracking(%s, self);", result_name);
|
|
}
|
|
Delete(result_name);
|
|
}
|
|
}
|
|
|
|
/* Emit the function call */
|
|
if (director_method) {
|
|
Printf(f->code, "try {\n");
|
|
}
|
|
|
|
Setattr(n, "wrap:name", wname);
|
|
|
|
Swig_director_emit_dynamic_cast(n, f);
|
|
String *actioncode = emit_action(n);
|
|
|
|
if (director_method) {
|
|
Printf(actioncode, "} catch (Swig::DirectorException& e) {\n");
|
|
Printf(actioncode, " rb_exc_raise(e.getError());\n");
|
|
Printf(actioncode, " SWIG_fail;\n");
|
|
Printf(actioncode, "}\n");
|
|
}
|
|
|
|
/* Return value if necessary */
|
|
if (SwigType_type(t) != 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(t, 0));
|
|
}
|
|
}
|
|
}
|
|
if (actioncode) {
|
|
Append(f->code, actioncode);
|
|
Delete(actioncode);
|
|
}
|
|
emit_return_variable(n, t, 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);
|
|
String *classtype = psmart ? psmart : t;
|
|
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(t) == 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);
|
|
|
|
/* Substitute the function name */
|
|
Replaceall(f->code, "$symname", symname);
|
|
|
|
/* Emit the function */
|
|
Wrapper_print(f, f_wrappers);
|
|
|
|
/* Now register the function with the interpreter */
|
|
if (!Swig_symbol_isoverloaded(n)) {
|
|
create_command(n, symname);
|
|
} else {
|
|
if (current == CONSTRUCTOR_ALLOCATE) {
|
|
create_command(n, symname);
|
|
} else {
|
|
if (!Getattr(n, "sym:nextSibling"))
|
|
dispatchFunction(n);
|
|
}
|
|
}
|
|
|
|
Delete(kwargs);
|
|
Delete(cleanup);
|
|
Delete(outarg);
|
|
DelWrapper(f);
|
|
Delete(symname);
|
|
|
|
return SWIG_OK;
|
|
}
|
|
|
|
/* ------------------------------------------------------------
|
|
* dispatchFunction()
|
|
* ------------------------------------------------------------ */
|
|
|
|
void dispatchFunction(Node *n) {
|
|
/* Last node in overloaded chain */
|
|
|
|
int maxargs;
|
|
String *tmp = NewString("");
|
|
String *dispatch = Swig_overload_dispatch(n, "return %s(nargs, args, self);", &maxargs);
|
|
|
|
/* Generate a dispatch wrapper for all overloaded functions */
|
|
|
|
Wrapper *f = NewWrapper();
|
|
String *symname = Getattr(n, "sym:name");
|
|
String *wname = Swig_name_wrapper(symname);
|
|
|
|
Printv(f->def, "SWIGINTERN VALUE ", wname, "(int nargs, VALUE *args, VALUE self) {", NIL);
|
|
|
|
Wrapper_add_local(f, "argc", "int argc");
|
|
bool ctor_director = (current == CONSTRUCTOR_INITIALIZE && Swig_directorclass(n));
|
|
if (current == MEMBER_FUNC || current == MEMBER_VAR || ctor_director) {
|
|
Printf(tmp, "VALUE argv[%d]", maxargs + 1);
|
|
} else {
|
|
Printf(tmp, "VALUE argv[%d]", maxargs);
|
|
}
|
|
Wrapper_add_local(f, "argv", tmp);
|
|
Wrapper_add_local(f, "ii", "int ii");
|
|
|
|
if (current == MEMBER_FUNC || current == MEMBER_VAR || ctor_director) {
|
|
maxargs += 1;
|
|
Printf(f->code, "argc = nargs + 1;\n");
|
|
Printf(f->code, "argv[0] = self;\n");
|
|
Printf(f->code, "if (argc > %d) SWIG_fail;\n", maxargs);
|
|
Printf(f->code, "for (ii = 1; (ii < argc); ++ii) {\n");
|
|
Printf(f->code, "argv[ii] = args[ii-1];\n");
|
|
Printf(f->code, "}\n");
|
|
} else {
|
|
Printf(f->code, "argc = nargs;\n");
|
|
Printf(f->code, "if (argc > %d) SWIG_fail;\n", maxargs);
|
|
Printf(f->code, "for (ii = 0; (ii < argc); ++ii) {\n");
|
|
Printf(f->code, "argv[ii] = args[ii];\n");
|
|
Printf(f->code, "}\n");
|
|
}
|
|
|
|
Replaceall(dispatch, "$args", "nargs, args, self");
|
|
Printv(f->code, dispatch, "\n", NIL);
|
|
|
|
|
|
|
|
// Generate prototype list, go to first node
|
|
Node *sibl = n;
|
|
|
|
while (Getattr(sibl, "sym:previousSibling"))
|
|
sibl = Getattr(sibl, "sym:previousSibling"); // go all the way up
|
|
|
|
// Constructors will be treated specially
|
|
String *siblNodeType = Getattr(sibl, "nodeType");
|
|
const bool isCtor = (Equal(siblNodeType, "constructor"));
|
|
const bool isMethod = (Equal(siblNodeType, "cdecl") && GetFlag(sibl, "ismember") && !isCtor);
|
|
|
|
// Construct real method name
|
|
String* methodName = NewString("");
|
|
if ( isMethod ) {
|
|
// Sometimes a method node has no parent (SF#3034054).
|
|
// This value is used in an exception message, so just skip the class
|
|
// name in this case so at least we don't segfault. This is probably
|
|
// just working around a problem elsewhere though.
|
|
Node *parent_node = parentNode(sibl);
|
|
if (parent_node)
|
|
Printv( methodName, Getattr(parent_node,"sym:name"), ".", NIL );
|
|
}
|
|
Append( methodName, Getattr(sibl,"sym:name" ) );
|
|
if ( isCtor ) Append( methodName, ".new" );
|
|
|
|
// Generate prototype list
|
|
String *protoTypes = NewString("");
|
|
do {
|
|
Append( protoTypes, "\n\" ");
|
|
if (!isCtor && !Equal(siblNodeType, "using")) {
|
|
SwigType *type = SwigType_str(Getattr(sibl, "type"), NULL);
|
|
Printv(protoTypes, type, " ", NIL);
|
|
Delete(type);
|
|
}
|
|
Printv(protoTypes, methodName, NIL );
|
|
Parm* p = Getattr(sibl, "wrap:parms");
|
|
if (p && (current == MEMBER_FUNC || current == MEMBER_VAR ||
|
|
ctor_director) )
|
|
p = nextSibling(p); // skip self
|
|
Append( protoTypes, "(" );
|
|
while(p)
|
|
{
|
|
Append( protoTypes, SwigType_str(Getattr(p,"type"), Getattr(p,"name")) );
|
|
if ( ( p = nextSibling(p)) ) Append(protoTypes, ", ");
|
|
}
|
|
Append( protoTypes, ")\\n\"" );
|
|
} while ((sibl = Getattr(sibl, "sym:nextSibling")));
|
|
|
|
Append(f->code, "fail:\n");
|
|
Printf(f->code, "Ruby_Format_OverloadedError( argc, %d, \"%s\", %s);\n",
|
|
maxargs, methodName, protoTypes);
|
|
Append(f->code, "\nreturn Qnil;\n");
|
|
|
|
Delete(methodName);
|
|
Delete(protoTypes);
|
|
|
|
Printv(f->code, "}\n", NIL);
|
|
Wrapper_print(f, f_wrappers);
|
|
create_command(n, Char(symname));
|
|
|
|
DelWrapper(f);
|
|
Delete(dispatch);
|
|
Delete(tmp);
|
|
Delete(wname);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* variableWrapper()
|
|
* --------------------------------------------------------------------- */
|
|
|
|
virtual int variableWrapper(Node *n) {
|
|
String* docs = docstring(n, AUTODOC_GETTER);
|
|
Printf(f_wrappers, "%s", docs);
|
|
Delete(docs);
|
|
|
|
|
|
char *name = GetChar(n, "name");
|
|
char *iname = GetChar(n, "sym:name");
|
|
SwigType *t = Getattr(n, "type");
|
|
String *tm;
|
|
String *getfname, *setfname;
|
|
Wrapper *getf, *setf;
|
|
int assignable = !is_immutable(n);
|
|
|
|
// Determine whether virtual global variables shall be used
|
|
// which have different getter and setter signatures,
|
|
// see https://docs.ruby-lang.org/en/2.6.0/extension_rdoc.html#label-Global+Variables+Shared+Between+C+and+Ruby
|
|
const bool use_virtual_var = (current == NO_CPP && useGlobalModule);
|
|
|
|
getf = NewWrapper();
|
|
setf = NewWrapper();
|
|
|
|
/* create getter */
|
|
int addfail = 0;
|
|
String *getname = Swig_name_get(NSPACE_TODO, iname);
|
|
getfname = Swig_name_wrapper(getname);
|
|
Setattr(n, "wrap:name", getfname);
|
|
Printv(getf->def, "SWIGINTERN VALUE\n", getfname, "(", NIL);
|
|
Printf(getf->def, (use_virtual_var) ? "ID id, VALUE *data" : "VALUE self");
|
|
Printf(getf->def, ") {");
|
|
Wrapper_add_local(getf, "_val", "VALUE _val");
|
|
|
|
tm = Swig_typemap_lookup("varout", n, name, 0);
|
|
if (tm) {
|
|
Replaceall(tm, "$result", "_val");
|
|
/* Printv(getf->code,tm, NIL); */
|
|
addfail = emit_action_code(n, getf->code, tm);
|
|
} else {
|
|
Swig_warning(WARN_TYPEMAP_VAROUT_UNDEF, input_file, line_number, "Unable to read variable of type %s\n", SwigType_str(t, 0));
|
|
}
|
|
Printv(getf->code, tab4, "return _val;\n", NIL);
|
|
if (addfail) {
|
|
Append(getf->code, "fail:\n");
|
|
Append(getf->code, " return Qnil;\n");
|
|
}
|
|
Append(getf->code, "}\n");
|
|
|
|
Wrapper_print(getf, f_wrappers);
|
|
|
|
if (!assignable) {
|
|
setfname = NewString("(rb_gvar_setter_t *)NULL");
|
|
} else {
|
|
/* create setter */
|
|
String* docs = docstring(n, AUTODOC_SETTER);
|
|
Printf(f_wrappers, "%s", docs);
|
|
Delete(docs);
|
|
|
|
String *setname = Swig_name_set(NSPACE_TODO, iname);
|
|
setfname = Swig_name_wrapper(setname);
|
|
Setattr(n, "wrap:name", setfname);
|
|
Printf(setf->def, "SWIGINTERN ");
|
|
if (use_virtual_var) {
|
|
Printv(setf->def, "void\n", setfname, "(VALUE _val, ID id, VALUE *data) {", NIL);
|
|
} else {
|
|
Printv(setf->def, "VALUE\n", setfname, "(VALUE self, VALUE _val) {", NIL);
|
|
}
|
|
tm = Swig_typemap_lookup("varin", n, name, 0);
|
|
if (tm) {
|
|
Replaceall(tm, "$input", "_val");
|
|
/* Printv(setf->code,tm,"\n",NIL); */
|
|
emit_action_code(n, setf->code, tm);
|
|
} else {
|
|
Swig_warning(WARN_TYPEMAP_VARIN_UNDEF, input_file, line_number, "Unable to set variable of type %s\n", SwigType_str(t, 0));
|
|
}
|
|
if (use_virtual_var) {
|
|
Printf(setf->code, "fail:\n");
|
|
Printv(setf->code, tab4, "return;\n", NIL);
|
|
} else {
|
|
Printv(setf->code, tab4, "return _val;\n", NIL);
|
|
Printf(setf->code, "fail:\n");
|
|
Printv(setf->code, tab4, "return Qnil;\n", NIL);
|
|
}
|
|
Printf(setf->code, "}\n");
|
|
Wrapper_print(setf, f_wrappers);
|
|
Delete(setname);
|
|
}
|
|
|
|
/* define accessor methods */
|
|
Insert(getfname, 0, "VALUEFUNC(");
|
|
Append(getfname, ")");
|
|
Insert(setfname, 0, (use_virtual_var) ? "SWIG_RUBY_VOID_ANYARGS_FUNC(" : "VALUEFUNC(");
|
|
Append(setfname, ")");
|
|
|
|
String *s = NewString("");
|
|
switch (current) {
|
|
case STATIC_VAR:
|
|
/* C++ class variable */
|
|
Printv(s, tab4, "rb_define_singleton_method(", klass->vname, ", \"", klass->strip(iname), "\", ", getfname, ", 0);\n", NIL);
|
|
if (assignable) {
|
|
Printv(s, tab4, "rb_define_singleton_method(", klass->vname, ", \"", klass->strip(iname), "=\", ", setfname, ", 1);\n", NIL);
|
|
}
|
|
Printv(klass->init, s, NIL);
|
|
break;
|
|
default:
|
|
/* C global variable */
|
|
/* wrapped in Ruby module attribute */
|
|
assert(current == NO_CPP);
|
|
if (!useGlobalModule) {
|
|
Printv(s, tab4, "rb_define_singleton_method(", modvar, ", \"", iname, "\", ", getfname, ", 0);\n", NIL);
|
|
if (assignable) {
|
|
Printv(s, tab4, "rb_define_singleton_method(", modvar, ", \"", iname, "=\", ", setfname, ", 1);\n", NIL);
|
|
}
|
|
} else {
|
|
Printv(s, tab4, "rb_define_virtual_variable(\"$", iname, "\", ", getfname, ", ", setfname, ");\n", NIL);
|
|
}
|
|
Printv(f_init, s, NIL);
|
|
Delete(s);
|
|
break;
|
|
}
|
|
Delete(getname);
|
|
Delete(getfname);
|
|
Delete(setfname);
|
|
DelWrapper(setf);
|
|
DelWrapper(getf);
|
|
return SWIG_OK;
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* validate_const_name(char *name)
|
|
*
|
|
* Validate constant name.
|
|
* --------------------------------------------------------------------- */
|
|
|
|
char *validate_const_name(char *name, const char *reason) {
|
|
if (!name || name[0] == '\0')
|
|
return name;
|
|
|
|
if (isupper(name[0]))
|
|
return name;
|
|
|
|
if (islower(name[0])) {
|
|
name[0] = (char)toupper(name[0]);
|
|
Swig_warning(WARN_RUBY_WRONG_NAME, input_file, line_number, "Wrong %s name (corrected to `%s')\n", reason, name);
|
|
return name;
|
|
}
|
|
|
|
Swig_warning(WARN_RUBY_WRONG_NAME, input_file, line_number, "Wrong %s name %s\n", reason, name);
|
|
|
|
return name;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* constantWrapper()
|
|
* --------------------------------------------------------------------- */
|
|
|
|
virtual int constantWrapper(Node *n) {
|
|
Swig_require("constantWrapper", n, "*sym:name", "type", "value", NIL);
|
|
|
|
char *iname = GetChar(n, "sym:name");
|
|
SwigType *type = Getattr(n, "type");
|
|
String *rawval = Getattr(n, "rawval");
|
|
String *value = rawval ? rawval : 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(psmart) : 0;
|
|
if (psmart) {
|
|
SwigType_add_pointer(psmart);
|
|
SwigType_remember(psmart);
|
|
}
|
|
String *bmangle = SwigType_manglestr(psmart ? psmart : btype);
|
|
if (multipleInheritance) {
|
|
Insert(bmangle, 0, "((swig_class *) SWIGTYPE");
|
|
Append(bmangle, "->clientdata)->mImpl");
|
|
Printv(klass->init, "rb_include_module(", klass->mImpl, ", ", bmangle, ");\n", NIL);
|
|
} else {
|
|
Insert(bmangle, 0, "((swig_class *) SWIGTYPE");
|
|
Append(bmangle, "->clientdata)->klass");
|
|
Replaceall(klass->init, "$super", bmangle);
|
|
}
|
|
Delete(bmangle);
|
|
Delete(psmart);
|
|
Delete(btype);
|
|
}
|
|
base = Next(base);
|
|
while (base.item && GetFlag(base.item, "feature:ignore")) {
|
|
base = Next(base);
|
|
}
|
|
if (!multipleInheritance) {
|
|
/* Warn about multiple inheritance for additional base class(es) */
|
|
while (base.item) {
|
|
if (GetFlag(base.item, "feature:ignore")) {
|
|
base = Next(base);
|
|
continue;
|
|
}
|
|
String *proxyclassname = SwigType_str(Getattr(n, "classtypeobj"), 0);
|
|
String *baseclassname = SwigType_str(Getattr(base.item, "name"), 0);
|
|
Swig_warning(WARN_RUBY_MULTIPLE_INHERITANCE, Getfile(n), Getline(n),
|
|
"Warning for %s, base %s ignored. Multiple inheritance is not supported in Ruby.\n", proxyclassname, baseclassname);
|
|
base = Next(base);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check to see if a %markfunc was specified.
|
|
*/
|
|
void handleMarkFuncDirective(Node *n) {
|
|
String *markfunc = Getattr(n, "feature:markfunc");
|
|
if (markfunc) {
|
|
Printf(klass->init, "SwigClass%s.mark = (void (*)(void *)) %s;\n", klass->name, markfunc);
|
|
} else {
|
|
Printf(klass->init, "SwigClass%s.mark = 0;\n", klass->name);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check to see if a %freefunc was specified.
|
|
*/
|
|
void handleFreeFuncDirective(Node *n) {
|
|
String *freefunc = Getattr(n, "feature:freefunc");
|
|
if (freefunc) {
|
|
Printf(klass->init, "SwigClass%s.destroy = (void (*)(void *)) %s;\n", klass->name, freefunc);
|
|
} else {
|
|
if (klass->destructor_defined) {
|
|
Printf(klass->init, "SwigClass%s.destroy = (void (*)(void *)) free_%s;\n", klass->name, klass->mname);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check to see if tracking is enabled for this class.
|
|
*/
|
|
void handleTrackDirective(Node *n) {
|
|
int trackObjects = GetFlag(n, "feature:trackobjects");
|
|
if (trackObjects) {
|
|
Printf(klass->init, "SwigClass%s.trackObjects = 1;\n", klass->name);
|
|
} else {
|
|
Printf(klass->init, "SwigClass%s.trackObjects = 0;\n", klass->name);
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* classHandler()
|
|
* ---------------------------------------------------------------------- */
|
|
|
|
virtual int classHandler(Node *n) {
|
|
String* docs = docstring(n, AUTODOC_CLASS);
|
|
Printf(f_wrappers, "%s", docs);
|
|
Delete(docs);
|
|
|
|
String *name = Getattr(n, "name");
|
|
String *symname = Getattr(n, "sym:name");
|
|
String *namestr = SwigType_namestr(name); // does template expansion
|
|
|
|
klass = RCLASS(classes, Char(namestr));
|
|
assert(klass != 0);
|
|
Delete(namestr);
|
|
String *valid_name = NewString(symname);
|
|
validate_const_name(Char(valid_name), "class");
|
|
|
|
Clear(klass->type);
|
|
Printv(klass->type, Getattr(n, "classtype"), NIL);
|
|
Printv(f_wrappers, "static swig_class SwigClass", valid_name, ";\n\n", NIL);
|
|
Printv(klass->init, "\n", tab4, NIL);
|
|
|
|
if (!useGlobalModule) {
|
|
Printv(klass->init, klass->vname, " = rb_define_class_under(", modvar, ", \"", klass->name, "\", $super);\n", NIL);
|
|
} else {
|
|
Printv(klass->init, klass->vname, " = rb_define_class(\"", klass->name,
|
|
"\", $super);\n", NIL);
|
|
}
|
|
|
|
if (multipleInheritance) {
|
|
Printv(klass->init, klass->mImpl, " = rb_define_module_under(", klass->vname, ", \"Impl\");\n", NIL);
|
|
}
|
|
|
|
SwigType *tt = NewString(name);
|
|
SwigType_add_pointer(tt);
|
|
SwigType_remember(tt);
|
|
SwigType *psmart = Getattr(n, "smart");
|
|
if (psmart) {
|
|
SwigType_add_pointer(psmart);
|
|
SwigType_remember(psmart);
|
|
}
|
|
String *tm = SwigType_manglestr(psmart ? psmart : tt);
|
|
Printf(klass->init, "SWIG_TypeClientData(SWIGTYPE%s, (void *) &SwigClass%s);\n", tm, valid_name);
|
|
Delete(tm);
|
|
Delete(psmart);
|
|
Delete(tt);
|
|
Delete(valid_name);
|
|
|
|
includeRubyModules(n);
|
|
|
|
Printv(klass->init, "$allocator", NIL);
|
|
Printv(klass->init, "$initializer", NIL);
|
|
|
|
Language::classHandler(n);
|
|
|
|
handleBaseClasses(n);
|
|
handleMarkFuncDirective(n);
|
|
handleFreeFuncDirective(n);
|
|
handleTrackDirective(n);
|
|
|
|
if (multipleInheritance) {
|
|
Printv(klass->init, "rb_include_module(", klass->vname, ", ", klass->mImpl, ");\n", NIL);
|
|
}
|
|
|
|
String *s = NewString("");
|
|
Printv(s, tab4, "rb_undef_alloc_func(", klass->vname, ");\n", NIL);
|
|
Replaceall(klass->init, "$allocator", s);
|
|
Replaceall(klass->init, "$initializer", "");
|
|
|
|
if (GetFlag(n, "feature:exceptionclass")) {
|
|
Replaceall(klass->init, "$super", "rb_eRuntimeError");
|
|
} else {
|
|
Replaceall(klass->init, "$super", "rb_cObject");
|
|
}
|
|
Delete(s);
|
|
|
|
Printv(f_init, klass->init, NIL);
|
|
klass = 0;
|
|
return SWIG_OK;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* memberfunctionHandler()
|
|
*
|
|
* Method for adding C++ member function
|
|
*
|
|
* By default, we're going to create a function of the form :
|
|
*
|
|
* Foo_bar(this,args)
|
|
*
|
|
* Where Foo is the classname, bar is the member name and the this pointer
|
|
* is explicitly attached to the beginning.
|
|
*
|
|
* The renaming only applies to the member function part, not the full
|
|
* classname.
|
|
*
|
|
* --------------------------------------------------------------------- */
|
|
|
|
virtual int memberfunctionHandler(Node *n) {
|
|
current = MEMBER_FUNC;
|
|
|
|
String* docs = docstring(n, AUTODOC_METHOD);
|
|
Printf(f_wrappers, "%s", docs);
|
|
Delete(docs);
|
|
|
|
Language::memberfunctionHandler(n);
|
|
current = NO_CPP;
|
|
return SWIG_OK;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* constructorHandler()
|
|
*
|
|
* Method for adding C++ member constructor
|
|
* -------------------------------------------------------------------- */
|
|
|
|
void set_director_ctor_code(Node *n) {
|
|
/* director ctor code is specific for each class */
|
|
Delete(director_prot_ctor_code);
|
|
director_prot_ctor_code = NewString("");
|
|
Node *pn = Swig_methodclass(n);
|
|
String *symname = Getattr(pn, "sym:name");
|
|
String *name = Copy(symname);
|
|
char *cname = Char(name);
|
|
if (cname)
|
|
cname[0] = (char)toupper(cname[0]);
|
|
Printv(director_prot_ctor_code,
|
|
"if ( $comparison ) { /* subclassed */\n",
|
|
" $director_new \n",
|
|
"} else {\n", " rb_raise(rb_eNameError,\"accessing abstract class or protected constructor\"); \n", " return Qnil;\n", "}\n", NIL);
|
|
Delete(director_ctor_code);
|
|
director_ctor_code = NewString("");
|
|
Printv(director_ctor_code, "if ( $comparison ) { /* subclassed */\n", " $director_new \n", "} else {\n", " $nondirector_new \n", "}\n", NIL);
|
|
Delete(name);
|
|
}
|
|
|
|
virtual int constructorHandler(Node *n) {
|
|
int use_director = Swig_directorclass(n);
|
|
if (use_director) {
|
|
set_director_ctor_code(n);
|
|
}
|
|
|
|
/* First wrap the allocate method */
|
|
current = CONSTRUCTOR_ALLOCATE;
|
|
Swig_name_register("construct", "%n%c_allocate");
|
|
|
|
Language::constructorHandler(n);
|
|
|
|
String* docs = docstring(n, AUTODOC_CTOR);
|
|
Printf(f_wrappers, "%s", docs);
|
|
Delete(docs);
|
|
|
|
/*
|
|
* If we're wrapping the constructor of a C++ director class, prepend a new parameter
|
|
* to receive the scripting language object (e.g. 'self')
|
|
*
|
|
*/
|
|
Swig_save("ruby:constructorHandler", n, "parms", NIL);
|
|
if (use_director) {
|
|
Parm *parms = Getattr(n, "parms");
|
|
Parm *self;
|
|
String *name = NewString("self");
|
|
String *type = NewString("VALUE");
|
|
self = NewParm(type, name, n);
|
|
Delete(type);
|
|
Delete(name);
|
|
Setattr(self, "lname", "Qnil");
|
|
if (parms)
|
|
set_nextSibling(self, parms);
|
|
Setattr(n, "parms", self);
|
|
Setattr(n, "wrap:self", "1");
|
|
Delete(self);
|
|
}
|
|
|
|
/* Now do the instance initialize method */
|
|
current = CONSTRUCTOR_INITIALIZE;
|
|
Swig_name_register("construct", "new_%n%c");
|
|
Language::constructorHandler(n);
|
|
|
|
/* Restore original parameter list */
|
|
Delattr(n, "wrap:self");
|
|
Swig_restore(n);
|
|
|
|
/* Done */
|
|
Swig_name_unregister("construct");
|
|
current = NO_CPP;
|
|
klass->constructor_defined = 1;
|
|
return SWIG_OK;
|
|
}
|
|
|
|
virtual int copyconstructorHandler(Node *n) {
|
|
int use_director = Swig_directorclass(n);
|
|
if (use_director) {
|
|
set_director_ctor_code(n);
|
|
}
|
|
|
|
/* First wrap the allocate method */
|
|
current = CONSTRUCTOR_ALLOCATE;
|
|
Swig_name_register("construct", "%n%c_allocate");
|
|
|
|
return Language::copyconstructorHandler(n);
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* destructorHandler()
|
|
* -------------------------------------------------------------------- */
|
|
|
|
virtual int destructorHandler(Node *n) {
|
|
|
|
/* Do no spit free function if user defined his own for this class */
|
|
Node *pn = Swig_methodclass(n);
|
|
String *freefunc = Getattr(pn, "feature:freefunc");
|
|
if (freefunc) return SWIG_OK;
|
|
|
|
current = DESTRUCTOR;
|
|
Language::destructorHandler(n);
|
|
|
|
freefunc = NewString("");
|
|
String *freebody = NewString("");
|
|
String *pname0 = Swig_cparm_name(0, 0);
|
|
|
|
Printv(freefunc, "free_", klass->mname, NIL);
|
|
Printv(freebody, "SWIGINTERN void\n", freefunc, "(void *self) {\n", NIL);
|
|
Printv(freebody, tab4, klass->type, " *", pname0, " = (", klass->type, " *)self;\n", NIL);
|
|
Printv(freebody, tab4, NIL);
|
|
|
|
/* Check to see if object tracking is activated for the class
|
|
that owns this destructor. */
|
|
if (GetFlag(pn, "feature:trackobjects")) {
|
|
Printf(freebody, "SWIG_RubyRemoveTracking(%s);\n", pname0);
|
|
Printv(freebody, tab4, NIL);
|
|
}
|
|
|
|
if (Extend) {
|
|
String *wrap = Getattr(n, "wrap:code");
|
|
if (wrap) {
|
|
Printv(f_wrappers, wrap, NIL);
|
|
}
|
|
/* Printv(freebody, Swig_name_destroy(name), "(", pname0, ")", NIL); */
|
|
Printv(freebody, Getattr(n, "wrap:action"), "\n", NIL);
|
|
} else {
|
|
String *action = Getattr(n, "wrap:action");
|
|
if (action) {
|
|
Printv(freebody, action, "\n", NIL);
|
|
} else {
|
|
/* In the case swig emits no destroy function. */
|
|
if (CPlusPlus)
|
|
Printf(freebody, "delete %s;\n", pname0);
|
|
else
|
|
Printf(freebody, "free((char*) %s);\n", pname0);
|
|
}
|
|
}
|
|
|
|
Printv(freebody, "}\n\n", NIL);
|
|
|
|
Printv(f_wrappers, freebody, NIL);
|
|
|
|
klass->destructor_defined = 1;
|
|
current = NO_CPP;
|
|
Delete(freefunc);
|
|
Delete(freebody);
|
|
Delete(pname0);
|
|
return SWIG_OK;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* membervariableHandler()
|
|
*
|
|
* This creates a pair of functions to set/get the variable of a member.
|
|
* -------------------------------------------------------------------- */
|
|
|
|
virtual int membervariableHandler(Node *n) {
|
|
String* docs = docstring(n, AUTODOC_GETTER);
|
|
Printf(f_wrappers, "%s", docs);
|
|
Delete(docs);
|
|
|
|
if (!is_immutable(n)) {
|
|
String* docs = docstring(n, AUTODOC_SETTER);
|
|
Printf(f_wrappers, "%s", docs);
|
|
Delete(docs);
|
|
}
|
|
|
|
current = MEMBER_VAR;
|
|
Language::membervariableHandler(n);
|
|
current = NO_CPP;
|
|
return SWIG_OK;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------
|
|
* staticmemberfunctionHandler()
|
|
*
|
|
* Wrap a static C++ function
|
|
* ---------------------------------------------------------------------- */
|
|
|
|
virtual int staticmemberfunctionHandler(Node *n) {
|
|
String* docs = docstring(n, AUTODOC_STATICFUNC);
|
|
Printf(f_wrappers, "%s", docs);
|
|
Delete(docs);
|
|
|
|
current = STATIC_FUNC;
|
|
Language::staticmemberfunctionHandler(n);
|
|
current = NO_CPP;
|
|
return SWIG_OK;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* memberconstantHandler()
|
|
*
|
|
* Create a C++ constant
|
|
* --------------------------------------------------------------------- */
|
|
|
|
virtual int memberconstantHandler(Node *n) {
|
|
String* docs = docstring(n, AUTODOC_STATICFUNC);
|
|
Printf(f_wrappers, "%s", docs);
|
|
Delete(docs);
|
|
|
|
current = CLASS_CONST;
|
|
Language::memberconstantHandler(n);
|
|
current = NO_CPP;
|
|
return SWIG_OK;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* staticmembervariableHandler()
|
|
* --------------------------------------------------------------------- */
|
|
|
|
virtual int staticmembervariableHandler(Node *n) {
|
|
String* docs = docstring(n, AUTODOC_GETTER);
|
|
Printf(f_wrappers, "%s", docs);
|
|
Delete(docs);
|
|
|
|
if (!is_immutable(n)) {
|
|
String* docs = docstring(n, AUTODOC_SETTER);
|
|
Printf(f_wrappers, "%s", docs);
|
|
Delete(docs);
|
|
}
|
|
|
|
current = STATIC_VAR;
|
|
Language::staticmembervariableHandler(n);
|
|
current = NO_CPP;
|
|
return SWIG_OK;
|
|
}
|
|
|
|
/* C++ director class generation */
|
|
virtual int classDirector(Node *n) {
|
|
return Language::classDirector(n);
|
|
}
|
|
|
|
virtual int classDirectorInit(Node *n) {
|
|
String *declaration;
|
|
declaration = Swig_director_declaration(n);
|
|
Printf(f_directors_h, "\n");
|
|
Printf(f_directors_h, "%s\n", declaration);
|
|
Printf(f_directors_h, "public:\n");
|
|
Delete(declaration);
|
|
return Language::classDirectorInit(n);
|
|
}
|
|
|
|
virtual int classDirectorEnd(Node *n) {
|
|
Printf(f_directors_h, "};\n\n");
|
|
return Language::classDirectorEnd(n);
|
|
}
|
|
|
|
/* ------------------------------------------------------------
|
|
* classDirectorConstructor()
|
|
* ------------------------------------------------------------ */
|
|
|
|
virtual int classDirectorConstructor(Node *n) {
|
|
Node *parent = Getattr(n, "parentNode");
|
|
String *sub = NewString("");
|
|
String *decl = Getattr(n, "decl");
|
|
String *supername = Swig_class_name(parent);
|
|
String *classname = NewString("");
|
|
Printf(classname, "SwigDirector_%s", supername);
|
|
|
|
/* insert self parameter */
|
|
Parm *p;
|
|
ParmList *superparms = Getattr(n, "parms");
|
|
ParmList *parms = CopyParmList(superparms);
|
|
String *type = NewString("VALUE");
|
|
p = NewParm(type, NewString("self"), n);
|
|
set_nextSibling(p, parms);
|
|
parms = p;
|
|
|
|
if (!Getattr(n, "defaultargs")) {
|
|
/* constructor */
|
|
{
|
|
Wrapper *w = NewWrapper();
|
|
String *call;
|
|
String *basetype = Getattr(parent, "classtype");
|
|
String *target = Swig_method_decl(0, decl, classname, parms, 0);
|
|
call = Swig_csuperclass_call(0, basetype, superparms);
|
|
Printf(w->def, "%s::%s: %s, Swig::Director(self) { }", classname, target, call);
|
|
Delete(target);
|
|
Wrapper_print(w, f_directors);
|
|
Delete(call);
|
|
DelWrapper(w);
|
|
}
|
|
|
|
/* constructor header */
|
|
{
|
|
String *target = Swig_method_decl(0, decl, classname, parms, 1);
|
|
Printf(f_directors_h, " %s;\n", target);
|
|
Delete(target);
|
|
}
|
|
}
|
|
|
|
Delete(sub);
|
|
Delete(classname);
|
|
Delete(supername);
|
|
Delete(parms);
|
|
return Language::classDirectorConstructor(n);
|
|
}
|
|
|
|
/* ------------------------------------------------------------
|
|
* classDirectorDefaultConstructor()
|
|
* ------------------------------------------------------------ */
|
|
|
|
virtual int classDirectorDefaultConstructor(Node *n) {
|
|
String *classname;
|
|
Wrapper *w;
|
|
classname = Swig_class_name(n);
|
|
w = NewWrapper();
|
|
Printf(w->def, "SwigDirector_%s::SwigDirector_%s(VALUE self) : Swig::Director(self) { }", classname, classname);
|
|
Wrapper_print(w, f_directors);
|
|
DelWrapper(w);
|
|
Printf(f_directors_h, " SwigDirector_%s(VALUE self);\n", classname);
|
|
Delete(classname);
|
|
return Language::classDirectorDefaultConstructor(n);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------
|
|
* exceptionSafeMethodCall()
|
|
*
|
|
* Emit a virtual director method to pass a method call on to the
|
|
* underlying Ruby instance.
|
|
*
|
|
* --------------------------------------------------------------- */
|
|
|
|
void exceptionSafeMethodCall(String *className, Node *n, Wrapper *w, int argc, String *args, bool initstack) {
|
|
Wrapper *body = NewWrapper();
|
|
Wrapper *rescue = NewWrapper();
|
|
|
|
String *methodName = Getattr(n, "sym:name");
|
|
|
|
String *bodyName = NewStringf("%s_%s_body", className, methodName);
|
|
String *rescueName = NewStringf("%s_%s_rescue", className, methodName);
|
|
String *depthCountName = NewStringf("%s_%s_call_depth", className, methodName);
|
|
|
|
// Check for an exception typemap of some kind
|
|
String *tm = Swig_typemap_lookup("director:except", n, Swig_cresult_name(), 0);
|
|
if (!tm) {
|
|
tm = Getattr(n, "feature:director:except");
|
|
}
|
|
|
|
if ((tm != 0) && (Len(tm) > 0) && (Strcmp(tm, "1") != 0)) {
|
|
// Declare a global to hold the depth count
|
|
if (!Getattr(n, "sym:nextSibling")) {
|
|
Printf(body->def, "static int %s = 0;\n", depthCountName);
|
|
|
|
// Function body
|
|
Printf(body->def, "VALUE %s(VALUE data) {\n", bodyName);
|
|
Wrapper_add_localv(body, "args", "Swig::body_args *", "args", "= reinterpret_cast<Swig::body_args *>(data)", NIL);
|
|
Wrapper_add_localv(body, Swig_cresult_name(), "VALUE", Swig_cresult_name(), "= Qnil", NIL);
|
|
Printf(body->code, "%s++;\n", depthCountName);
|
|
Printv(body->code, Swig_cresult_name(), " = rb_funcall2(args->recv, args->id, args->argc, args->argv);\n", NIL);
|
|
Printf(body->code, "%s--;\n", depthCountName);
|
|
Printv(body->code, "return ", Swig_cresult_name(), ";\n", NIL);
|
|
Printv(body->code, "}", NIL);
|
|
|
|
// Exception handler
|
|
Printf(rescue->def, "VALUE %s(VALUE args, VALUE error) {\n", rescueName);
|
|
Replaceall(tm, "$error", "error");
|
|
Printf(rescue->code, "%s--;\n", depthCountName);
|
|
Printf(rescue->code, "if (%s == 0) ", depthCountName);
|
|
Printv(rescue->code, Str(tm), "\n", NIL);
|
|
Printv(rescue->code, "rb_exc_raise(error);\n", NIL);
|
|
Printv(rescue->code, "return Qnil;\n", NIL);
|
|
Printv(rescue->code, "}", NIL);
|
|
}
|
|
|
|
// Main code
|
|
Wrapper_add_localv(w, "args", "Swig::body_args", "args", NIL);
|
|
Wrapper_add_localv(w, "status", "int", "status", NIL);
|
|
Printv(w->code, "args.recv = swig_get_self();\n", NIL);
|
|
Printf(w->code, "args.id = rb_intern(\"%s\");\n", methodName);
|
|
Printf(w->code, "args.argc = %d;\n", argc);
|
|
if (argc > 0) {
|
|
Printf(w->code, "args.argv = new VALUE[%d];\n", argc);
|
|
for (int i = 0; i < argc; i++) {
|
|
Printf(w->code, "args.argv[%d] = obj%d;\n", i, i);
|
|
}
|
|
} else {
|
|
Printv(w->code, "args.argv = 0;\n", NIL);
|
|
}
|
|
Printf(w->code, "%s = rb_protect(PROTECTFUNC(%s), reinterpret_cast<VALUE>(&args), &status);\n", Swig_cresult_name(), bodyName);
|
|
if ( initstack ) Printf(w->code, "SWIG_RELEASE_STACK;\n");
|
|
Printf(w->code, "if (status) {\n");
|
|
Printf(w->code, "VALUE lastErr = rb_gv_get(\"$!\");\n");
|
|
Printf(w->code, "%s(reinterpret_cast<VALUE>(&args), lastErr);\n", rescueName);
|
|
Printf(w->code, "}\n");
|
|
if (argc > 0) {
|
|
Printv(w->code, "delete [] args.argv;\n", NIL);
|
|
}
|
|
// Dump wrapper code
|
|
Wrapper_print(body, f_directors_helpers);
|
|
Wrapper_print(rescue, f_directors_helpers);
|
|
} else {
|
|
if (argc > 0) {
|
|
Printf(w->code, "%s = rb_funcall(swig_get_self(), rb_intern(\"%s\"), %d%s);\n", Swig_cresult_name(), methodName, argc, args);
|
|
} else {
|
|
Printf(w->code, "%s = rb_funcall(swig_get_self(), rb_intern(\"%s\"), 0, Qnil);\n", Swig_cresult_name(), methodName);
|
|
}
|
|
if ( initstack ) Printf(w->code, "SWIG_RELEASE_STACK;\n");
|
|
}
|
|
|
|
// Clean up
|
|
Delete(bodyName);
|
|
Delete(rescueName);
|
|
Delete(depthCountName);
|
|
DelWrapper(body);
|
|
DelWrapper(rescue);
|
|
}
|
|
|
|
virtual int classDirectorMethod(Node *n, Node *parent, String *super) {
|
|
int is_void = 0;
|
|
int is_pointer = 0;
|
|
String *decl = Getattr(n, "decl");
|
|
String *name = Getattr(n, "name");
|
|
String *classname = Getattr(parent, "sym:name");
|
|
String *c_classname = Getattr(parent, "name");
|
|
String *symname = Getattr(n, "sym:name");
|
|
String *declaration = NewString("");
|
|
ParmList *l = Getattr(n, "parms");
|
|
Wrapper *w = NewWrapper();
|
|
String *tm;
|
|
String *wrap_args = NewString("");
|
|
String *returntype = Getattr(n, "type");
|
|
Parm *p;
|
|
String *value = Getattr(n, "value");
|
|
String *storage = Getattr(n, "storage");
|
|
bool pure_virtual = false;
|
|
int status = SWIG_OK;
|
|
int idx;
|
|
bool ignored_method = GetFlag(n, "feature:ignore") ? true : false;
|
|
bool asvoid = checkAttribute( n, "feature:numoutputs", "0") ? true : false;
|
|
bool initstack = checkAttribute( n, "feature:initstack", "1") ? true : false;
|
|
|
|
if (Cmp(storage, "virtual") == 0) {
|
|
if (Cmp(value, "0") == 0) {
|
|
pure_virtual = true;
|
|
}
|
|
}
|
|
String *overnametmp = NewString(Getattr(n, "sym:name"));
|
|
if (Getattr(n, "sym:overloaded")) {
|
|
Printf(overnametmp, "::%s", Getattr(n, "sym:overname"));
|
|
}
|
|
|
|
/* determine if the method returns a pointer */
|
|
is_pointer = SwigType_ispointer_return(decl);
|
|
is_void = (!Cmp(returntype, "void") && !is_pointer);
|
|
|
|
/* virtual method definition */
|
|
String *target;
|
|
String *pclassname = NewStringf("SwigDirector_%s", classname);
|
|
String *qualified_name = NewStringf("%s::%s", pclassname, name);
|
|
SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : Getattr(n, "classDirectorMethods:type");
|
|
target = Swig_method_decl(rtype, decl, qualified_name, l, 0);
|
|
Printf(w->def, "%s", target);
|
|
Delete(qualified_name);
|
|
Delete(target);
|
|
/* header declaration */
|
|
target = Swig_method_decl(rtype, decl, name, l, 1);
|
|
Printf(declaration, " virtual %s", target);
|
|
Delete(target);
|
|
|
|
// Get any exception classes in the throws typemap
|
|
if (Getattr(n, "noexcept")) {
|
|
Append(w->def, " noexcept");
|
|
Append(declaration, " noexcept");
|
|
}
|
|
ParmList *throw_parm_list = 0;
|
|
|
|
if ((throw_parm_list = Getattr(n, "throws")) || Getattr(n, "throw")) {
|
|
Parm *p;
|
|
int gencomma = 0;
|
|
|
|
Append(w->def, " throw(");
|
|
Append(declaration, " throw(");
|
|
|
|
if (throw_parm_list)
|
|
Swig_typemap_attach_parms("throws", throw_parm_list, 0);
|
|
for (p = throw_parm_list; p; p = nextSibling(p)) {
|
|
if (Getattr(p, "tmap:throws")) {
|
|
if (gencomma++) {
|
|
Append(w->def, ", ");
|
|
Append(declaration, ", ");
|
|
}
|
|
|
|
Printf(w->def, "%s", SwigType_str(Getattr(p, "type"), 0));
|
|
Printf(declaration, "%s", SwigType_str(Getattr(p, "type"), 0));
|
|
}
|
|
}
|
|
|
|
Append(w->def, ")");
|
|
Append(declaration, ")");
|
|
}
|
|
|
|
Append(w->def, " {");
|
|
Append(declaration, ";\n");
|
|
|
|
if (initstack && !(ignored_method && !pure_virtual)) {
|
|
Append(w->def, "\nSWIG_INIT_STACK;\n");
|
|
}
|
|
|
|
/* declare method return value
|
|
* if the return value is a reference or const reference, a specialized typemap must
|
|
* handle it, including declaration of c_result ($result).
|
|
*/
|
|
if (!is_void && (!ignored_method || pure_virtual)) {
|
|
if (!SwigType_isclass(returntype)) {
|
|
if (!(SwigType_ispointer(returntype) || SwigType_isreference(returntype))) {
|
|
String *construct_result = NewStringf("= SwigValueInit< %s >()", SwigType_lstr(returntype, 0));
|
|
Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), construct_result, NIL);
|
|
Delete(construct_result);
|
|
} else {
|
|
Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), "= 0", NIL);
|
|
}
|
|
} else {
|
|
String *cres = SwigType_lstr(returntype, "c_result");
|
|
Printf(w->code, "%s;\n", cres);
|
|
Delete(cres);
|
|
}
|
|
}
|
|
|
|
if (ignored_method) {
|
|
if (!pure_virtual) {
|
|
if (!is_void)
|
|
Printf(w->code, "return ");
|
|
String *super_call = Swig_method_call(super, l);
|
|
Printf(w->code, "%s;\n", super_call);
|
|
Delete(super_call);
|
|
} else {
|
|
Printf(w->code, "Swig::DirectorPureVirtualException::raise(\"Attempted to invoke pure virtual method %s::%s\");\n", SwigType_namestr(c_classname),
|
|
SwigType_namestr(name));
|
|
}
|
|
} else {
|
|
/* attach typemaps to arguments (C/C++ -> Ruby) */
|
|
String *arglist = NewString("");
|
|
|
|
Swig_director_parms_fixup(l);
|
|
|
|
Swig_typemap_attach_parms("in", l, 0);
|
|
Swig_typemap_attach_parms("directorin", l, w);
|
|
Swig_typemap_attach_parms("directorargout", l, w);
|
|
|
|
char source[256];
|
|
|
|
int outputs = 0;
|
|
if (!is_void && !asvoid)
|
|
outputs++;
|
|
|
|
/* build argument list and type conversion string */
|
|
idx = 0; p = l;
|
|
while ( p ) {
|
|
if (Getattr(p, "tmap:directorargout") != 0)
|
|
outputs++;
|
|
|
|
if ( checkAttribute( p, "tmap:in:numinputs", "0") ) {
|
|
p = Getattr(p, "tmap:in:next");
|
|
continue;
|
|
}
|
|
|
|
String *parameterName = Getattr(p, "name");
|
|
String *parameterType = Getattr(p, "type");
|
|
|
|
Putc(',', arglist);
|
|
if ((tm = Getattr(p, "tmap:directorin")) != 0) {
|
|
sprintf(source, "obj%d", idx++);
|
|
String *input = NewString(source);
|
|
Setattr(p, "emit:directorinput", input);
|
|
Replaceall(tm, "$input", input);
|
|
Replaceall(tm, "$owner", "0");
|
|
Delete(input);
|
|
Printv(wrap_args, tm, "\n", NIL);
|
|
Wrapper_add_localv(w, source, "VALUE", source, "= Qnil", NIL);
|
|
Printv(arglist, source, NIL);
|
|
p = Getattr(p, "tmap:directorin:next");
|
|
continue;
|
|
} else if (Cmp(parameterType, "void")) {
|
|
/**
|
|
* Special handling for pointers to other C++ director classes.
|
|
* Ideally this would be left to a typemap, but there is currently no
|
|
* way to selectively apply the dynamic_cast<> to classes that have
|
|
* directors. In other words, the type "SwigDirector_$1_lname" only exists
|
|
* for classes with directors. We avoid the problem here by checking
|
|
* module.wrap::directormap, but it's not clear how to get a typemap to
|
|
* do something similar. Perhaps a new default typemap (in addition
|
|
* to SWIGTYPE) called DIRECTORTYPE?
|
|
*/
|
|
if (SwigType_ispointer(parameterType) || SwigType_isreference(parameterType)) {
|
|
Node *modname = Getattr(parent, "module");
|
|
Node *target = Swig_directormap(modname, parameterType);
|
|
sprintf(source, "obj%d", idx++);
|
|
String *nonconst = 0;
|
|
/* strip pointer/reference --- should move to Swig/stype.c */
|
|
String *nptype = NewString(Char(parameterType) + 2);
|
|
/* name as pointer */
|
|
String *ppname = Copy(parameterName);
|
|
if (SwigType_isreference(parameterType)) {
|
|
Insert(ppname, 0, "&");
|
|
}
|
|
/* if necessary, cast away const since Ruby doesn't support it! */
|
|
if (SwigType_isconst(nptype)) {
|
|
nonconst = NewStringf("nc_tmp_%s", parameterName);
|
|
String *nonconst_i = NewStringf("= const_cast< %s >(%s)", SwigType_lstr(parameterType, 0), ppname);
|
|
Wrapper_add_localv(w, nonconst, SwigType_lstr(parameterType, 0), nonconst, nonconst_i, NIL);
|
|
Delete(nonconst_i);
|
|
Swig_warning(WARN_LANG_DISCARD_CONST, input_file, line_number,
|
|
"Target language argument '%s' discards const in director method %s::%s.\n", SwigType_str(parameterType, parameterName),
|
|
SwigType_namestr(c_classname), SwigType_namestr(name));
|
|
} else {
|
|
nonconst = Copy(ppname);
|
|
}
|
|
Delete(nptype);
|
|
Delete(ppname);
|
|
String *mangle = SwigType_manglestr(parameterType);
|
|
if (target) {
|
|
String *director = NewStringf("director_%s", mangle);
|
|
Wrapper_add_localv(w, director, "Swig::Director *", director, "= 0", NIL);
|
|
Wrapper_add_localv(w, source, "VALUE", source, "= Qnil", NIL);
|
|
Printf(wrap_args, "%s = dynamic_cast<Swig::Director *>(%s);\n", director, nonconst);
|
|
Printf(wrap_args, "if (!%s) {\n", director);
|
|
Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE%s, 0);\n", source, nonconst, mangle);
|
|
Printf(wrap_args, "} else {\n");
|
|
Printf(wrap_args, "%s = %s->swig_get_self();\n", source, director);
|
|
Printf(wrap_args, "}\n");
|
|
Delete(director);
|
|
Printv(arglist, source, NIL);
|
|
} else {
|
|
Wrapper_add_localv(w, source, "VALUE", source, "= Qnil", NIL);
|
|
Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE%s, 0);\n", source, nonconst, mangle);
|
|
//Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE_p_%s, 0);\n",
|
|
// source, nonconst, base);
|
|
Printv(arglist, source, NIL);
|
|
}
|
|
Delete(mangle);
|
|
Delete(nonconst);
|
|
} else {
|
|
Swig_warning(WARN_TYPEMAP_DIRECTORIN_UNDEF, input_file, line_number,
|
|
"Unable to use type %s as a function argument in director method %s::%s (skipping method).\n", SwigType_str(parameterType, 0),
|
|
SwigType_namestr(c_classname), SwigType_namestr(name));
|
|
status = SWIG_NOWRAP;
|
|
break;
|
|
}
|
|
}
|
|
p = nextSibling(p);
|
|
}
|
|
|
|
/* declare Ruby return value */
|
|
String *value_result = NewStringf("VALUE SWIGUNUSED %s", Swig_cresult_name());
|
|
Wrapper_add_local(w, Swig_cresult_name(), value_result);
|
|
Delete(value_result);
|
|
|
|
/* wrap complex arguments to VALUEs */
|
|
Printv(w->code, wrap_args, NIL);
|
|
|
|
/* pass the method call on to the Ruby object */
|
|
exceptionSafeMethodCall(classname, n, w, idx, arglist, initstack);
|
|
|
|
/*
|
|
* Ruby method may return a simple object, or an Array of objects.
|
|
* For in/out arguments, we have to extract the appropriate VALUEs from the Array,
|
|
* then marshal everything back to C/C++ (return value and output arguments).
|
|
*/
|
|
|
|
/* Marshal return value and other outputs (if any) from VALUE to C/C++ type */
|
|
|
|
String *cleanup = NewString("");
|
|
String *outarg = NewString("");
|
|
|
|
if (outputs > 1) {
|
|
Wrapper_add_local(w, "output", "VALUE output");
|
|
Printf(w->code, "if (TYPE(%s) != T_ARRAY) {\n", Swig_cresult_name());
|
|
Printf(w->code, "Ruby_DirectorTypeMismatchException(\"Ruby method failed to return an array.\");\n");
|
|
Printf(w->code, "}\n");
|
|
}
|
|
|
|
idx = 0;
|
|
|
|
/* Marshal return value */
|
|
if (!is_void) {
|
|
tm = Swig_typemap_lookup("directorout", n, Swig_cresult_name(), w);
|
|
if (tm != 0) {
|
|
if (outputs > 1 && !asvoid ) {
|
|
Printf(w->code, "output = rb_ary_entry(%s, %d);\n", Swig_cresult_name(), idx++);
|
|
Replaceall(tm, "$input", "output");
|
|
} else {
|
|
Replaceall(tm, "$input", Swig_cresult_name());
|
|
}
|
|
/* TODO check this */
|
|
if (Getattr(n, "wrap:disown")) {
|
|
Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
|
|
} else {
|
|
Replaceall(tm, "$disown", "0");
|
|
}
|
|
Replaceall(tm, "$result", "c_result");
|
|
Printv(w->code, tm, "\n", NIL);
|
|
} else {
|
|
Swig_warning(WARN_TYPEMAP_DIRECTOROUT_UNDEF, input_file, line_number,
|
|
"Unable to use return type %s in director method %s::%s (skipping method).\n", SwigType_str(returntype, 0),
|
|
SwigType_namestr(c_classname), SwigType_namestr(name));
|
|
status = SWIG_ERROR;
|
|
}
|
|
}
|
|
|
|
/* Marshal outputs */
|
|
for (p = l; p;) {
|
|
if ((tm = Getattr(p, "tmap:directorargout")) != 0) {
|
|
if (outputs > 1) {
|
|
Printf(w->code, "output = rb_ary_entry(%s, %d);\n", Swig_cresult_name(), idx++);
|
|
Replaceall(tm, "$result", "output");
|
|
} else {
|
|
Replaceall(tm, "$result", Swig_cresult_name());
|
|
}
|
|
Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
|
|
Printv(w->code, tm, "\n", NIL);
|
|
p = Getattr(p, "tmap:directorargout:next");
|
|
} else {
|
|
p = nextSibling(p);
|
|
}
|
|
}
|
|
|
|
Delete(arglist);
|
|
Delete(cleanup);
|
|
Delete(outarg);
|
|
}
|
|
|
|
/* any existing helper functions to handle this? */
|
|
if (!is_void) {
|
|
if (!(ignored_method && !pure_virtual)) {
|
|
String *rettype = SwigType_str(returntype, 0);
|
|
if (!SwigType_isreference(returntype)) {
|
|
Printf(w->code, "return (%s) c_result;\n", rettype);
|
|
} else {
|
|
Printf(w->code, "return (%s) *c_result;\n", rettype);
|
|
}
|
|
Delete(rettype);
|
|
}
|
|
}
|
|
|
|
Printf(w->code, "}\n");
|
|
|
|
// We expose protected methods via an extra public inline method which makes a straight call to the wrapped class' method
|
|
String *inline_extra_method = NewString("");
|
|
if (dirprot_mode() && !is_public(n) && !pure_virtual) {
|
|
Printv(inline_extra_method, declaration, NIL);
|
|
String *extra_method_name = NewStringf("%sSwigPublic", name);
|
|
Replaceall(inline_extra_method, name, extra_method_name);
|
|
Replaceall(inline_extra_method, ";\n", " {\n ");
|
|
if (!is_void)
|
|
Printf(inline_extra_method, "return ");
|
|
String *methodcall = Swig_method_call(super, l);
|
|
Printv(inline_extra_method, methodcall, ";\n }\n", NIL);
|
|
Delete(methodcall);
|
|
Delete(extra_method_name);
|
|
}
|
|
|
|
/* emit the director method */
|
|
if (status == SWIG_OK) {
|
|
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:
|
|
*/
|