mirror of https://github.com/swig/swig
1587 lines
52 KiB
C++
1587 lines
52 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.
|
|
*
|
|
* allocate.cxx
|
|
*
|
|
* This module also has two main purposes modifying the parse tree.
|
|
*
|
|
* First, it is responsible for adding in using declarations from base class
|
|
* members into the parse tree.
|
|
*
|
|
* Second, after each class declaration, it analyses if the class/struct supports
|
|
* default constructors and destructors in C++. There are several rules that
|
|
* define this behavior including pure abstract methods, private sections,
|
|
* and non-default constructors in base classes. See the ARM or
|
|
* Doc/Manual/SWIGPlus.html for details.
|
|
*
|
|
* Once the analysis is complete, the non-explicit/implied default constructors
|
|
* and destructors are added to the parse tree. Implied copy constructors are
|
|
* added too if requested via the copyctor feature. Detection of implied
|
|
* assignment operators is also handled as assigment is required in the generated
|
|
* code for variable setters.
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
#include "swigmod.h"
|
|
#include "cparse.h"
|
|
|
|
static int virtual_elimination_mode = 0; /* set to 0 on default */
|
|
|
|
/* Set virtual_elimination_mode */
|
|
void Wrapper_virtual_elimination_mode_set(int flag) {
|
|
virtual_elimination_mode = flag;
|
|
}
|
|
|
|
/* Helper function to assist with abstract class checking.
|
|
This is a major hack. Sorry. */
|
|
|
|
extern "C" {
|
|
static String *search_decl = 0; /* Declarator being searched */
|
|
static Node *check_implemented(Node *n) {
|
|
String *decl;
|
|
if (!n)
|
|
return 0;
|
|
while (n) {
|
|
if (Strcmp(nodeType(n), "cdecl") == 0) {
|
|
decl = Getattr(n, "decl");
|
|
if (SwigType_isfunction(decl)) {
|
|
SwigType *decl1 = SwigType_typedef_resolve_all(decl);
|
|
SwigType *decl2 = SwigType_pop_function(decl1);
|
|
if (Strcmp(decl2, search_decl) == 0) {
|
|
if (!GetFlag(n, "abstract")) {
|
|
Delete(decl1);
|
|
Delete(decl2);
|
|
return n;
|
|
}
|
|
}
|
|
Delete(decl1);
|
|
Delete(decl2);
|
|
}
|
|
}
|
|
n = Getattr(n, "csym:nextSibling");
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
class Allocate:public Dispatcher {
|
|
Node *inclass;
|
|
int extendmode;
|
|
|
|
/* Checks if a function, n, is the same as any in the base class, ie if the method is polymorphic.
|
|
* Also checks for methods which will be hidden (ie a base has an identical non-virtual method).
|
|
* Both methods must have public access for a match to occur. */
|
|
int function_is_defined_in_bases(Node *n, Node *bases) {
|
|
|
|
if (!bases)
|
|
return 0;
|
|
|
|
String *this_decl = Getattr(n, "decl");
|
|
if (!this_decl)
|
|
return 0;
|
|
|
|
String *name = Getattr(n, "name");
|
|
String *this_type = Getattr(n, "type");
|
|
String *resolved_decl = SwigType_typedef_resolve_all(this_decl);
|
|
|
|
// Search all base classes for methods with same signature
|
|
for (int i = 0; i < Len(bases); i++) {
|
|
Node *b = Getitem(bases, i);
|
|
Node *base = firstChild(b);
|
|
while (base) {
|
|
if (Strcmp(nodeType(base), "extend") == 0) {
|
|
// Loop through all the %extend methods
|
|
Node *extend = firstChild(base);
|
|
while (extend) {
|
|
if (function_is_defined_in_bases_seek(n, b, extend, this_decl, name, this_type, resolved_decl)) {
|
|
Delete(resolved_decl);
|
|
return 1;
|
|
}
|
|
extend = nextSibling(extend);
|
|
}
|
|
} else if (Strcmp(nodeType(base), "using") == 0) {
|
|
// Loop through all the using declaration methods
|
|
Node *usingdecl = firstChild(base);
|
|
while (usingdecl) {
|
|
if (function_is_defined_in_bases_seek(n, b, usingdecl, this_decl, name, this_type, resolved_decl)) {
|
|
Delete(resolved_decl);
|
|
return 1;
|
|
}
|
|
usingdecl = nextSibling(usingdecl);
|
|
}
|
|
} else {
|
|
// normal methods
|
|
if (function_is_defined_in_bases_seek(n, b, base, this_decl, name, this_type, resolved_decl)) {
|
|
Delete(resolved_decl);
|
|
return 1;
|
|
}
|
|
}
|
|
base = nextSibling(base);
|
|
}
|
|
}
|
|
Delete(resolved_decl);
|
|
resolved_decl = 0;
|
|
for (int j = 0; j < Len(bases); j++) {
|
|
Node *b = Getitem(bases, j);
|
|
if (function_is_defined_in_bases(n, Getattr(b, "allbases")))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Helper function for function_is_defined_in_bases */
|
|
int function_is_defined_in_bases_seek(Node *n, Node *b, Node *base, String *this_decl, String *name, String *this_type, String *resolved_decl) {
|
|
|
|
String *base_decl = Getattr(base, "decl");
|
|
SwigType *base_type = Getattr(base, "type");
|
|
if (base_decl && base_type) {
|
|
if (checkAttribute(base, "name", name) && !GetFlag(b, "feature:ignore") /* whole class is ignored */ ) {
|
|
if (SwigType_isfunction(resolved_decl) && SwigType_isfunction(base_decl)) {
|
|
// We have found a method that has the same name as one in a base class
|
|
bool covariant_returntype = false;
|
|
bool returntype_match = Strcmp(base_type, this_type) == 0 ? true : false;
|
|
bool decl_match = Strcmp(base_decl, this_decl) == 0 ? true : false;
|
|
if (returntype_match && decl_match) {
|
|
// Exact match - we have found a method with identical signature
|
|
// No typedef resolution was done, but skipping it speeds things up slightly
|
|
} else {
|
|
// Either we have:
|
|
// 1) matching methods but are one of them uses a different typedef (return type or parameter) to the one in base class' method
|
|
// 2) matching polymorphic methods with covariant return type
|
|
// 3) a non-matching method (ie an overloaded method of some sort)
|
|
// 4) a matching method which is not polymorphic, ie it hides the base class' method
|
|
|
|
// Check if fully resolved return types match (including
|
|
// covariant return types)
|
|
if (!returntype_match) {
|
|
String *this_returntype = function_return_type(n);
|
|
String *base_returntype = function_return_type(base);
|
|
returntype_match = Strcmp(this_returntype, base_returntype) == 0 ? true : false;
|
|
if (!returntype_match) {
|
|
covariant_returntype = SwigType_issubtype(this_returntype, base_returntype) ? true : false;
|
|
returntype_match = covariant_returntype;
|
|
}
|
|
Delete(this_returntype);
|
|
Delete(base_returntype);
|
|
}
|
|
// The return types must match at this point, for the whole method to match
|
|
if (returntype_match && !decl_match) {
|
|
// Now need to check the parameter list
|
|
// First do an inexpensive parameter count
|
|
ParmList *this_parms = Getattr(n, "parms");
|
|
ParmList *base_parms = Getattr(base, "parms");
|
|
if (ParmList_len(this_parms) == ParmList_len(base_parms)) {
|
|
// Number of parameters are the same, now check that all the parameters match
|
|
SwigType *base_fn = NewString("");
|
|
SwigType *this_fn = NewString("");
|
|
SwigType_add_function(base_fn, base_parms);
|
|
SwigType_add_function(this_fn, this_parms);
|
|
base_fn = SwigType_typedef_resolve_all(base_fn);
|
|
this_fn = SwigType_typedef_resolve_all(this_fn);
|
|
if (Strcmp(base_fn, this_fn) == 0) {
|
|
// Finally check that the qualifiers match
|
|
int base_qualifier = SwigType_isqualifier(resolved_decl);
|
|
int this_qualifier = SwigType_isqualifier(base_decl);
|
|
if (base_qualifier == this_qualifier) {
|
|
decl_match = true;
|
|
}
|
|
}
|
|
Delete(base_fn);
|
|
Delete(this_fn);
|
|
}
|
|
}
|
|
}
|
|
//Printf(stderr,"look %s %s %d %d\n",base_decl, this_decl, returntype_match, decl_match);
|
|
|
|
if (decl_match && returntype_match) {
|
|
// Found an identical method in the base class
|
|
bool this_wrapping_protected_members = is_member_director(n) ? true : false; // This should really check for dirprot rather than just being a director method
|
|
bool base_wrapping_protected_members = is_member_director(base) ? true : false; // This should really check for dirprot rather than just being a director method
|
|
bool both_have_public_access = is_public(n) && is_public(base);
|
|
bool both_have_protected_access = (is_protected(n) && this_wrapping_protected_members) && (is_protected(base) && base_wrapping_protected_members);
|
|
bool both_have_private_access = is_private(n) && is_private(base);
|
|
if (checkAttribute(base, "storage", "virtual")) {
|
|
// Found a polymorphic method.
|
|
// Mark the polymorphic method, in case the virtual keyword was not used.
|
|
Setattr(n, "storage", "virtual");
|
|
if (!GetFlag(b, "feature:interface")) { // interface implementation neither hides nor overrides
|
|
if (both_have_public_access || both_have_protected_access) {
|
|
if (!is_non_public_base(inclass, b))
|
|
Setattr(n, "override", base); // Note C# definition of override, ie access must be the same
|
|
}
|
|
else if (!both_have_private_access) {
|
|
// Different access
|
|
if (this_wrapping_protected_members || base_wrapping_protected_members)
|
|
if (!is_non_public_base(inclass, b))
|
|
Setattr(n, "hides", base); // Note C# definition of hiding, ie hidden if access is different
|
|
}
|
|
}
|
|
// Try and find the most base's covariant return type
|
|
SwigType *most_base_covariant_type = Getattr(base, "covariant");
|
|
if (!most_base_covariant_type && covariant_returntype)
|
|
most_base_covariant_type = function_return_type(base, false);
|
|
|
|
if (!most_base_covariant_type) {
|
|
// Eliminate the derived virtual method.
|
|
if (virtual_elimination_mode && !is_member_director(n))
|
|
if (both_have_public_access)
|
|
if (!is_non_public_base(inclass, b))
|
|
if (!Swig_symbol_isoverloaded(n)) {
|
|
// Don't eliminate if an overloaded method as this hides the method
|
|
// in the scripting languages: the dispatch function will hide the base method if ignored.
|
|
SetFlag(n, "feature:ignore");
|
|
}
|
|
} else {
|
|
// Some languages need to know about covariant return types
|
|
Setattr(n, "covariant", most_base_covariant_type);
|
|
}
|
|
|
|
} else {
|
|
// Found an identical method in the base class, but it is not polymorphic.
|
|
if (both_have_public_access || both_have_protected_access)
|
|
if (!is_non_public_base(inclass, b))
|
|
Setattr(n, "hides", base);
|
|
}
|
|
if (both_have_public_access || both_have_protected_access)
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Determines whether the base class, b, is in the list of private
|
|
* or protected base classes for class n. */
|
|
bool is_non_public_base(Node *n, Node *b) {
|
|
bool non_public_base = false;
|
|
Node *bases = Getattr(n, "privatebases");
|
|
if (bases) {
|
|
for (int i = 0; i < Len(bases); i++) {
|
|
Node *base = Getitem(bases, i);
|
|
if (base == b)
|
|
non_public_base = true;
|
|
}
|
|
}
|
|
bases = Getattr(n, "protectedbases");
|
|
if (bases) {
|
|
for (int i = 0; i < Len(bases); i++) {
|
|
Node *base = Getitem(bases, i);
|
|
if (base == b)
|
|
non_public_base = true;
|
|
}
|
|
}
|
|
return non_public_base;
|
|
}
|
|
|
|
/* Returns the return type for a function. The node n should be a function.
|
|
If resolve is true the fully returned type is fully resolved.
|
|
Caller is responsible for deleting returned string. */
|
|
String *function_return_type(Node *n, bool resolve = true) {
|
|
String *decl = Getattr(n, "decl");
|
|
SwigType *type = Getattr(n, "type");
|
|
String *ty = NewString(type);
|
|
SwigType_push(ty, decl);
|
|
if (SwigType_isqualifier(ty))
|
|
Delete(SwigType_pop(ty));
|
|
Delete(SwigType_pop_function(ty));
|
|
if (resolve) {
|
|
String *unresolved = ty;
|
|
ty = SwigType_typedef_resolve_all(unresolved);
|
|
Delete(unresolved);
|
|
}
|
|
return ty;
|
|
}
|
|
|
|
/* Checks if a class member is the same as inherited from the class bases */
|
|
int class_member_is_defined_in_bases(Node *member, Node *classnode) {
|
|
Node *bases; /* bases is the closest ancestors of classnode */
|
|
int defined = 0;
|
|
|
|
bases = Getattr(classnode, "allbases");
|
|
if (!bases)
|
|
return 0;
|
|
|
|
{
|
|
int old_mode = virtual_elimination_mode;
|
|
if (is_member_director(classnode, member))
|
|
virtual_elimination_mode = 0;
|
|
|
|
if (function_is_defined_in_bases(member, bases)) {
|
|
defined = 1;
|
|
}
|
|
|
|
virtual_elimination_mode = old_mode;
|
|
}
|
|
|
|
if (defined)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Checks to see if a class is abstract through inheritance,
|
|
and saves the first node that seems to be abstract.
|
|
*/
|
|
int is_abstract_inherit(Node *n, Node *base = 0, int first = 0) {
|
|
if (!first && (base == n))
|
|
return 0;
|
|
if (!base) {
|
|
/* Root node */
|
|
Symtab *stab = Getattr(n, "symtab"); /* Get symbol table for node */
|
|
Symtab *oldtab = Swig_symbol_setscope(stab);
|
|
int ret = is_abstract_inherit(n, n, 1);
|
|
Swig_symbol_setscope(oldtab);
|
|
return ret;
|
|
}
|
|
List *abstracts = Getattr(base, "abstracts");
|
|
if (abstracts) {
|
|
int dabstract = 0;
|
|
int len = Len(abstracts);
|
|
for (int i = 0; i < len; i++) {
|
|
Node *nn = Getitem(abstracts, i);
|
|
String *name = Getattr(nn, "name");
|
|
if (!name)
|
|
continue;
|
|
if (Strchr(name, '~'))
|
|
continue; /* Don't care about destructors */
|
|
String *base_decl = Getattr(nn, "decl");
|
|
if (base_decl)
|
|
base_decl = SwigType_typedef_resolve_all(base_decl);
|
|
if (SwigType_isfunction(base_decl))
|
|
search_decl = SwigType_pop_function(base_decl);
|
|
Node *dn = Swig_symbol_clookup_local_check(name, 0, check_implemented);
|
|
Delete(search_decl);
|
|
Delete(base_decl);
|
|
|
|
if (!dn) {
|
|
List *nabstracts = Getattr(n, "abstracts");
|
|
if (!nabstracts) {
|
|
nabstracts = NewList();
|
|
Setattr(n, "abstracts", nabstracts);
|
|
Delete(nabstracts);
|
|
}
|
|
Append(nabstracts, nn);
|
|
if (!Getattr(n, "abstracts:firstnode")) {
|
|
Setattr(n, "abstracts:firstnode", nn);
|
|
}
|
|
dabstract = base != n;
|
|
}
|
|
}
|
|
if (dabstract)
|
|
return 1;
|
|
}
|
|
List *bases = Getattr(base, "allbases");
|
|
if (!bases)
|
|
return 0;
|
|
for (int i = 0; i < Len(bases); i++) {
|
|
if (is_abstract_inherit(n, Getitem(bases, i))) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Grab methods used by smart pointers */
|
|
|
|
List *smart_pointer_methods(Node *cls, List *methods, int isconst, String *classname = 0) {
|
|
if (!methods) {
|
|
methods = NewList();
|
|
}
|
|
|
|
Node *c = firstChild(cls);
|
|
|
|
while (c) {
|
|
if (Getattr(c, "error") || GetFlag(c, "feature:ignore")) {
|
|
c = nextSibling(c);
|
|
continue;
|
|
}
|
|
if (!isconst && (Strcmp(nodeType(c), "extend") == 0)) {
|
|
methods = smart_pointer_methods(c, methods, isconst, Getattr(cls, "name"));
|
|
} else if (Strcmp(nodeType(c), "cdecl") == 0) {
|
|
if (!GetFlag(c, "feature:ignore")) {
|
|
String *storage = Getattr(c, "storage");
|
|
if (!((Cmp(storage, "typedef") == 0))
|
|
&& !Strstr(storage, "friend")) {
|
|
String *name = Getattr(c, "name");
|
|
String *symname = Getattr(c, "sym:name");
|
|
Node *e = Swig_symbol_clookup_local(name, 0);
|
|
if (e && is_public(e) && !GetFlag(e, "feature:ignore") && (Cmp(symname, Getattr(e, "sym:name")) == 0)) {
|
|
Swig_warning(WARN_LANG_DEREF_SHADOW, Getfile(e), Getline(e), "Declaration of '%s' shadows declaration accessible via operator->(),\n", name);
|
|
Swig_warning(WARN_LANG_DEREF_SHADOW, Getfile(c), Getline(c), "previous declaration of '%s'.\n", name);
|
|
} else {
|
|
/* Make sure node with same name doesn't already exist */
|
|
int k;
|
|
int match = 0;
|
|
for (k = 0; k < Len(methods); k++) {
|
|
e = Getitem(methods, k);
|
|
if (Cmp(symname, Getattr(e, "sym:name")) == 0) {
|
|
match = 1;
|
|
break;
|
|
}
|
|
if (!Getattr(e, "sym:name") && (Cmp(name, Getattr(e, "name")) == 0)) {
|
|
match = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!match) {
|
|
Node *cc = c;
|
|
while (cc) {
|
|
Node *cp = cc;
|
|
if (classname) {
|
|
Setattr(cp, "extendsmartclassname", classname);
|
|
}
|
|
Setattr(cp, "allocate:smartpointeraccess", "1");
|
|
/* If constant, we have to be careful */
|
|
if (isconst) {
|
|
SwigType *decl = Getattr(cp, "decl");
|
|
if (decl) {
|
|
if (SwigType_isfunction(decl)) { /* If method, we only add if it's a const method */
|
|
if (SwigType_isconst(decl)) {
|
|
Append(methods, cp);
|
|
}
|
|
} else {
|
|
Append(methods, cp);
|
|
}
|
|
} else {
|
|
Append(methods, cp);
|
|
}
|
|
} else {
|
|
Append(methods, cp);
|
|
}
|
|
cc = Getattr(cc, "sym:nextSibling");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
c = nextSibling(c);
|
|
}
|
|
/* Look for methods in base classes */
|
|
{
|
|
Node *bases = Getattr(cls, "bases");
|
|
int k;
|
|
for (k = 0; k < Len(bases); k++) {
|
|
smart_pointer_methods(Getitem(bases, k), methods, isconst);
|
|
}
|
|
}
|
|
/* Remove protected/private members */
|
|
{
|
|
for (int i = 0; i < Len(methods);) {
|
|
Node *n = Getitem(methods, i);
|
|
if (!is_public(n)) {
|
|
Delitem(methods, i);
|
|
continue;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
return methods;
|
|
}
|
|
|
|
void mark_exception_classes(ParmList *p) {
|
|
while (p) {
|
|
SwigType *ty = Getattr(p, "type");
|
|
SwigType *t = SwigType_typedef_resolve_all(ty);
|
|
if (SwigType_isreference(t) || SwigType_ispointer(t) || SwigType_isarray(t)) {
|
|
Delete(SwigType_pop(t));
|
|
}
|
|
Node *c = Swig_symbol_clookup(t, 0);
|
|
if (c) {
|
|
if (!GetFlag(c, "feature:exceptionclass")) {
|
|
SetFlag(c, "feature:exceptionclass");
|
|
}
|
|
}
|
|
p = nextSibling(p);
|
|
Delete(t);
|
|
}
|
|
}
|
|
|
|
|
|
void process_exceptions(Node *n) {
|
|
ParmList *catchlist = 0;
|
|
/*
|
|
the "catchlist" attribute is used to emit the block
|
|
|
|
try {$action;}
|
|
catch <list of catches>;
|
|
|
|
in emit.cxx
|
|
|
|
and is either constructed from the "feature:catches" feature
|
|
or copied from the node "throws" list.
|
|
*/
|
|
String *scatchlist = Getattr(n, "feature:catches");
|
|
if (scatchlist) {
|
|
catchlist = Swig_cparse_parms(scatchlist, n);
|
|
if (catchlist) {
|
|
Setattr(n, "catchlist", catchlist);
|
|
mark_exception_classes(catchlist);
|
|
Delete(catchlist);
|
|
}
|
|
}
|
|
ParmList *throws = Getattr(n, "throws");
|
|
if (throws) {
|
|
/* if there is no explicit catchlist, we catch everything in the throws list */
|
|
if (!catchlist) {
|
|
Setattr(n, "catchlist", throws);
|
|
}
|
|
mark_exception_classes(throws);
|
|
}
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* clone_member_for_using_declaration()
|
|
*
|
|
* Create a new member (constructor or method) by copying it from member c, ready
|
|
* for adding as a child to the using declaration node n.
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
Node *clone_member_for_using_declaration(Node *c, Node *n) {
|
|
Node *parent = parentNode(n);
|
|
String *decl = Getattr(c, "decl");
|
|
String *symname = Getattr(n, "sym:name");
|
|
int match = 0;
|
|
|
|
Node *over = Getattr(n, "sym:overloaded");
|
|
while (over) {
|
|
String *odecl = Getattr(over, "decl");
|
|
if (Cmp(decl, odecl) == 0) {
|
|
match = 1;
|
|
break;
|
|
}
|
|
over = Getattr(over, "sym:nextSibling");
|
|
}
|
|
|
|
if (match) {
|
|
/* Don't generate a method if the method is overridden in this class,
|
|
* for example don't generate another m(bool) should there be a Base::m(bool) :
|
|
* struct Derived : Base {
|
|
* void m(bool);
|
|
* using Base::m;
|
|
* };
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
Node *nn = copyNode(c);
|
|
Setfile(nn, Getfile(n));
|
|
Setline(nn, Getline(n));
|
|
if (!Getattr(nn, "sym:name"))
|
|
Setattr(nn, "sym:name", symname);
|
|
Symtab *st = Getattr(n, "sym:symtab");
|
|
assert(st);
|
|
Setattr(nn, "sym:symtab", st);
|
|
// The real parent is the "using" declaration node, but subsequent code generally handles
|
|
// and expects a class member to point to the parent class node
|
|
Setattr(nn, "parentNode", parent);
|
|
|
|
if (Equal(nodeType(c), "constructor")) {
|
|
Setattr(nn, "name", Getattr(n, "name"));
|
|
Setattr(nn, "sym:name", Getattr(n, "sym:name"));
|
|
// Note that the added constructor's access is the same as that of
|
|
// the base class' constructor not of the using declaration.
|
|
// It has already been set correctly and should not be changed.
|
|
} else {
|
|
// Access might be different from the method in the base class
|
|
Delattr(nn, "access");
|
|
Setattr(nn, "access", Getattr(n, "access"));
|
|
}
|
|
|
|
if (!GetFlag(nn, "feature:ignore")) {
|
|
ParmList *parms = CopyParmList(Getattr(c, "parms"));
|
|
int is_pointer = SwigType_ispointer_return(Getattr(nn, "decl"));
|
|
int is_void = checkAttribute(nn, "type", "void") && !is_pointer;
|
|
Setattr(nn, "parms", parms);
|
|
Delete(parms);
|
|
if (Getattr(n, "feature:extend")) {
|
|
String *ucode = is_void ? NewStringf("{ self->%s(", Getattr(n, "uname")) : NewStringf("{ return self->%s(", Getattr(n, "uname"));
|
|
|
|
for (ParmList *p = parms; p;) {
|
|
Append(ucode, Getattr(p, "name"));
|
|
p = nextSibling(p);
|
|
if (p)
|
|
Append(ucode, ",");
|
|
}
|
|
Append(ucode, "); }");
|
|
Setattr(nn, "code", ucode);
|
|
Delete(ucode);
|
|
}
|
|
ParmList *throw_parm_list = Getattr(c, "throws");
|
|
if (throw_parm_list)
|
|
Setattr(nn, "throws", CopyParmList(throw_parm_list));
|
|
} else {
|
|
Delete(nn);
|
|
nn = 0;
|
|
}
|
|
return nn;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* add_member_for_using_declaration()
|
|
*
|
|
* Add a new member (constructor or method) by copying it from member c.
|
|
* Add it into the linked list of members under the using declaration n (ccount,
|
|
* unodes and last_unodes are used for this).
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
void add_member_for_using_declaration(Node *c, Node *n, int &ccount, Node *&unodes, Node *&last_unodes) {
|
|
if (!(Swig_storage_isstatic(c)
|
|
|| checkAttribute(c, "storage", "typedef")
|
|
|| Strstr(Getattr(c, "storage"), "friend")
|
|
|| (Getattr(c, "feature:extend") && !Getattr(c, "code"))
|
|
|| GetFlag(c, "feature:ignore"))) {
|
|
|
|
String *symname = Getattr(n, "sym:name");
|
|
String *csymname = Getattr(c, "sym:name");
|
|
Node *parent = parentNode(n);
|
|
bool using_inherited_constructor_symname_okay = Equal(nodeType(c), "constructor") && Equal(symname, Getattr(parent, "sym:name"));
|
|
if (!csymname || Equal(csymname, symname) || using_inherited_constructor_symname_okay) {
|
|
Node *nn = clone_member_for_using_declaration(c, n);
|
|
if (nn) {
|
|
ccount++;
|
|
if (!last_unodes) {
|
|
last_unodes = nn;
|
|
unodes = nn;
|
|
} else {
|
|
Setattr(nn, "previousSibling", last_unodes);
|
|
Setattr(last_unodes, "nextSibling", nn);
|
|
Setattr(nn, "sym:previousSibling", last_unodes);
|
|
Setattr(last_unodes, "sym:nextSibling", nn);
|
|
Setattr(nn, "sym:overloaded", unodes);
|
|
Setattr(unodes, "sym:overloaded", unodes);
|
|
last_unodes = nn;
|
|
}
|
|
}
|
|
} else {
|
|
Swig_warning(WARN_LANG_USING_NAME_DIFFERENT, Getfile(n), Getline(n), "Using declaration %s, with name '%s', is not actually using\n", SwigType_namestr(Getattr(n, "uname")), symname);
|
|
Swig_warning(WARN_LANG_USING_NAME_DIFFERENT, Getfile(c), Getline(c), "the method from %s, with name '%s', as the names are different.\n", Swig_name_decl(c), csymname);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool is_assignable_type(const SwigType *type) {
|
|
bool assignable = true;
|
|
if (SwigType_type(type) == T_USER) {
|
|
Node *cn = Swig_symbol_clookup(type, 0);
|
|
if (cn) {
|
|
if ((Strcmp(nodeType(cn), "class") == 0)) {
|
|
if (Getattr(cn, "allocate:noassign")) {
|
|
assignable = false;
|
|
}
|
|
}
|
|
}
|
|
} else if (SwigType_isarray(type)) {
|
|
SwigType *array_type = SwigType_array_type(type);
|
|
assignable = is_assignable_type(array_type);
|
|
}
|
|
return assignable;
|
|
}
|
|
|
|
bool is_assignable(Node *n, bool &is_reference, bool &is_const) {
|
|
SwigType *ty = Copy(Getattr(n, "type"));
|
|
SwigType_push(ty, Getattr(n, "decl"));
|
|
SwigType *ftd = SwigType_typedef_resolve_all(ty);
|
|
SwigType *td = SwigType_strip_qualifiers(ftd);
|
|
|
|
bool assignable = is_assignable_type(td);
|
|
is_reference = SwigType_isreference(td) || SwigType_isrvalue_reference(td);
|
|
is_const = !SwigType_ismutable(ftd);
|
|
if (GetFlag(n, "hasconsttype"))
|
|
is_const = true;
|
|
|
|
Delete(ty);
|
|
Delete(ftd);
|
|
Delete(td);
|
|
return assignable;
|
|
}
|
|
|
|
public:
|
|
Allocate():
|
|
inclass(NULL), extendmode(0) {
|
|
}
|
|
|
|
virtual int top(Node *n) {
|
|
cplus_mode = PUBLIC;
|
|
inclass = 0;
|
|
extendmode = 0;
|
|
emit_children(n);
|
|
return SWIG_OK;
|
|
}
|
|
|
|
virtual int importDirective(Node *n) {
|
|
return emit_children(n);
|
|
}
|
|
virtual int includeDirective(Node *n) {
|
|
return emit_children(n);
|
|
}
|
|
virtual int externDeclaration(Node *n) {
|
|
return emit_children(n);
|
|
}
|
|
virtual int namespaceDeclaration(Node *n) {
|
|
return emit_children(n);
|
|
}
|
|
virtual int extendDirective(Node *n) {
|
|
extendmode = 1;
|
|
emit_children(n);
|
|
extendmode = 0;
|
|
return SWIG_OK;
|
|
}
|
|
|
|
virtual int classDeclaration(Node *n) {
|
|
Symtab *symtab = Swig_symbol_current();
|
|
Swig_symbol_setscope(Getattr(n, "symtab"));
|
|
save_value<Node*> oldInclass(inclass);
|
|
save_value<AccessMode> oldAcessMode(cplus_mode);
|
|
save_value<int> oldExtendMode(extendmode);
|
|
if (Getattr(n, "template"))
|
|
extendmode = 0;
|
|
if (!CPlusPlus) {
|
|
/* Always have default constructors/destructors in C */
|
|
Setattr(n, "allocate:default_constructor", "1");
|
|
Setattr(n, "allocate:default_destructor", "1");
|
|
}
|
|
|
|
if (Getattr(n, "allocate:visit")) {
|
|
Swig_symbol_setscope(symtab);
|
|
return SWIG_OK;
|
|
}
|
|
Setattr(n, "allocate:visit", "1");
|
|
|
|
/* Always visit base classes first */
|
|
{
|
|
List *bases = Getattr(n, "bases");
|
|
if (bases) {
|
|
for (int i = 0; i < Len(bases); i++) {
|
|
Node *b = Getitem(bases, i);
|
|
classDeclaration(b);
|
|
}
|
|
}
|
|
}
|
|
inclass = n;
|
|
String *kind = Getattr(n, "kind");
|
|
if (Strcmp(kind, "class") == 0) {
|
|
cplus_mode = PRIVATE;
|
|
} else {
|
|
cplus_mode = PUBLIC;
|
|
}
|
|
|
|
emit_children(n);
|
|
|
|
/* Check if the class is abstract via inheritance. This might occur if a class didn't have
|
|
any pure virtual methods of its own, but it didn't implement all of the pure methods in
|
|
a base class */
|
|
if (!Getattr(n, "abstracts") && is_abstract_inherit(n)) {
|
|
if (((Getattr(n, "allocate:public_constructor") || (!GetFlag(n, "feature:nodefault") && !Getattr(n, "allocate:has_constructor"))))) {
|
|
if (!GetFlag(n, "feature:notabstract")) {
|
|
Node *na = Getattr(n, "abstracts:firstnode");
|
|
if (na) {
|
|
Swig_warning(WARN_TYPE_ABSTRACT, Getfile(n), Getline(n),
|
|
"Class '%s' might be abstract, " "no constructors generated,\n", SwigType_namestr(Getattr(n, "name")));
|
|
Swig_warning(WARN_TYPE_ABSTRACT, Getfile(na), Getline(na), "Method %s might not be implemented.\n", Swig_name_decl(na));
|
|
if (!Getattr(n, "abstracts")) {
|
|
List *abstracts = NewList();
|
|
Append(abstracts, na);
|
|
Setattr(n, "abstracts", abstracts);
|
|
Delete(abstracts);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Getattr(n, "allocate:has_constructor")) {
|
|
/* No constructor is defined. We need to check a few things */
|
|
/* If class is abstract. No default constructor. Sorry */
|
|
if (Getattr(n, "abstracts")) {
|
|
Delattr(n, "allocate:default_constructor");
|
|
}
|
|
if (!Getattr(n, "allocate:default_constructor")) {
|
|
// No default constructor if either the default constructor or copy constructor is declared as deleted
|
|
if (!GetFlag(n, "allocate:deleted_default_constructor") && !GetFlag(n, "allocate:deleted_copy_constructor")) {
|
|
/* Check base classes */
|
|
List *bases = Getattr(n, "allbases");
|
|
int allows_default = 1;
|
|
|
|
for (int i = 0; i < Len(bases); i++) {
|
|
Node *n = Getitem(bases, i);
|
|
/* If base class does not allow default constructor, we don't allow it either */
|
|
if (!Getattr(n, "allocate:default_constructor") && (!Getattr(n, "allocate:default_base_constructor"))) {
|
|
allows_default = 0;
|
|
}
|
|
/* not constructible if base destructor is deleted */
|
|
if (Getattr(n, "allocate:deleted_default_destructor")) {
|
|
allows_default = 0;
|
|
}
|
|
}
|
|
if (allows_default) {
|
|
Setattr(n, "allocate:default_constructor", "1");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Getattr(n, "allocate:has_copy_constructor")) {
|
|
if (Getattr(n, "abstracts")) {
|
|
Delattr(n, "allocate:copy_constructor");
|
|
Delattr(n, "allocate:copy_constructor_non_const");
|
|
}
|
|
if (!Getattr(n, "allocate:copy_constructor")) {
|
|
// No copy constructor if the copy constructor is declared as deleted
|
|
if (!GetFlag(n, "allocate:deleted_copy_constructor")) {
|
|
/* Check base classes */
|
|
List *bases = Getattr(n, "allbases");
|
|
int allows_copy = 1;
|
|
int must_be_copy_non_const = 0;
|
|
|
|
for (int i = 0; i < Len(bases); i++) {
|
|
Node *n = Getitem(bases, i);
|
|
/* If base class does not allow copy constructor, we don't allow it either */
|
|
if (!Getattr(n, "allocate:copy_constructor") && (!Getattr(n, "allocate:copy_base_constructor"))) {
|
|
allows_copy = 0;
|
|
}
|
|
/* not constructible if base destructor is deleted */
|
|
if (Getattr(n, "allocate:deleted_default_destructor")) {
|
|
allows_copy = 0;
|
|
}
|
|
if (Getattr(n, "allocate:copy_constructor_non_const") || (Getattr(n, "allocate:copy_base_constructor_non_const"))) {
|
|
must_be_copy_non_const = 1;
|
|
}
|
|
}
|
|
if (allows_copy) {
|
|
Setattr(n, "allocate:copy_constructor", "1");
|
|
}
|
|
if (must_be_copy_non_const) {
|
|
Setattr(n, "allocate:copy_constructor_non_const", "1");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Getattr(n, "allocate:has_destructor")) {
|
|
/* No destructor was defined */
|
|
/* No destructor if the destructor is declared as deleted */
|
|
if (!GetFlag(n, "allocate:deleted_default_destructor")) {
|
|
/* Check base classes */
|
|
List *bases = Getattr(n, "allbases");
|
|
int allows_destruct = 1;
|
|
|
|
for (int i = 0; i < Len(bases); i++) {
|
|
Node *n = Getitem(bases, i);
|
|
/* If base class does not allow default destructor, we don't allow it either */
|
|
if (!Getattr(n, "allocate:default_destructor") && (!Getattr(n, "allocate:default_base_destructor"))) {
|
|
allows_destruct = 0;
|
|
}
|
|
}
|
|
if (allows_destruct) {
|
|
Setattr(n, "allocate:default_destructor", "1");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Getattr(n, "allocate:has_assign")) {
|
|
/* No assignment operator was defined */
|
|
List *bases = Getattr(n, "allbases");
|
|
int allows_assign = 1;
|
|
|
|
for (int i = 0; i < Len(bases); i++) {
|
|
Node *n = Getitem(bases, i);
|
|
/* If base class does not allow assignment, we don't allow it either */
|
|
if (Getattr(n, "allocate:noassign")) {
|
|
allows_assign = 0;
|
|
}
|
|
}
|
|
/* If any member variables are non assignable, this class is also non assignable by default */
|
|
if (GetFlag(n, "allocate:has_nonassignable")) {
|
|
allows_assign = 0;
|
|
}
|
|
if (!allows_assign) {
|
|
Setattr(n, "allocate:noassign", "1");
|
|
}
|
|
}
|
|
|
|
if (!Getattr(n, "allocate:has_new")) {
|
|
/* No new operator was defined */
|
|
List *bases = Getattr(n, "allbases");
|
|
int allows_new = 1;
|
|
|
|
for (int i = 0; i < Len(bases); i++) {
|
|
Node *n = Getitem(bases, i);
|
|
/* If base class does not allow new operator, we don't allow it either */
|
|
if (Getattr(n, "allocate:has_new")) {
|
|
allows_new = !Getattr(n, "allocate:nonew");
|
|
}
|
|
}
|
|
if (!allows_new) {
|
|
Setattr(n, "allocate:nonew", "1");
|
|
}
|
|
}
|
|
|
|
/* Check if base classes allow smart pointers, but might be hidden */
|
|
if (!Getattr(n, "allocate:smartpointer")) {
|
|
Node *sp = Swig_symbol_clookup("operator ->", 0);
|
|
if (sp) {
|
|
/* Look for parent */
|
|
Node *p = parentNode(sp);
|
|
if (Strcmp(nodeType(p), "extend") == 0) {
|
|
p = parentNode(p);
|
|
}
|
|
if (Strcmp(nodeType(p), "class") == 0) {
|
|
if (GetFlag(p, "feature:ignore")) {
|
|
Setattr(n, "allocate:smartpointer", Getattr(p, "allocate:smartpointer"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Swig_interface_propagate_methods(n);
|
|
|
|
/* Only care about default behavior. Remove temporary values */
|
|
Setattr(n, "allocate:visit", "1");
|
|
Swig_symbol_setscope(symtab);
|
|
|
|
/* Now we can add the additional implied constructors and destructors to the parse tree */
|
|
if (!ImportMode && !GetFlag(n, "feature:ignore")) {
|
|
int dir = 0;
|
|
if (Swig_directors_enabled()) {
|
|
int ndir = GetFlag(n, "feature:director");
|
|
int nndir = GetFlag(n, "feature:nodirector");
|
|
/* 'nodirector' has precedence over 'director' */
|
|
dir = (ndir || nndir) ? (ndir && !nndir) : 0;
|
|
}
|
|
int abstract = !dir && abstractClassTest(n);
|
|
int odefault = !GetFlag(n, "feature:nodefault");
|
|
|
|
/* default constructor */
|
|
if (!abstract && !GetFlag(n, "feature:nodefaultctor") && odefault) {
|
|
if (!Getattr(n, "allocate:has_constructor") && Getattr(n, "allocate:default_constructor")) {
|
|
addDefaultConstructor(n);
|
|
}
|
|
}
|
|
/* copy constructor */
|
|
if (CPlusPlus && !abstract && GetFlag(n, "feature:copyctor")) {
|
|
if (!Getattr(n, "allocate:has_copy_constructor") && Getattr(n, "allocate:copy_constructor")) {
|
|
addCopyConstructor(n);
|
|
}
|
|
}
|
|
/* default destructor */
|
|
if (!GetFlag(n, "feature:nodefaultdtor") && odefault) {
|
|
if (!Getattr(n, "allocate:has_destructor") && Getattr(n, "allocate:default_destructor")) {
|
|
addDestructor(n);
|
|
}
|
|
}
|
|
}
|
|
|
|
return SWIG_OK;
|
|
}
|
|
|
|
virtual int accessDeclaration(Node *n) {
|
|
cplus_mode = accessModeFromString(Getattr(n, "kind"));
|
|
return SWIG_OK;
|
|
}
|
|
|
|
virtual int usingDeclaration(Node *n) {
|
|
|
|
if (!Getattr(n, "namespace")) {
|
|
Node *ns;
|
|
/* using id */
|
|
Symtab *stab = Getattr(n, "sym:symtab");
|
|
if (stab) {
|
|
String *uname = Getattr(n, "uname");
|
|
ns = Swig_symbol_clookup(uname, stab);
|
|
if (!ns && SwigType_istemplate(uname)) {
|
|
String *tmp = Swig_symbol_template_deftype(uname, 0);
|
|
if (!Equal(tmp, uname)) {
|
|
ns = Swig_symbol_clookup(tmp, stab);
|
|
}
|
|
Delete(tmp);
|
|
}
|
|
} else {
|
|
ns = 0;
|
|
}
|
|
if (!ns) {
|
|
if (is_public(n)) {
|
|
Swig_warning(WARN_PARSE_USING_UNDEF, Getfile(n), Getline(n), "Nothing known about '%s'.\n", SwigType_namestr(Getattr(n, "uname")));
|
|
}
|
|
} else if (Equal(nodeType(ns), "constructor") && !GetFlag(n, "usingctor")) {
|
|
Swig_warning(WARN_PARSE_USING_CONSTRUCTOR, Getfile(n), Getline(n), "Using declaration '%s' for inheriting constructors uses base '%s' which is not an immediate base of '%s'.\n", SwigType_namestr(Getattr(n, "uname")), SwigType_namestr(Getattr(ns, "name")), SwigType_namestr(Getattr(parentNode(n), "name")));
|
|
} else {
|
|
if (inclass && !GetFlag(n, "feature:ignore") && Getattr(n, "sym:name")) {
|
|
{
|
|
String *ntype = nodeType(ns);
|
|
if (Equal(ntype, "cdecl") || Equal(ntype, "constructor") || Equal(ntype, "template")) {
|
|
/* Add a new class member to the parse tree (copy it from the base class member pointed to by the using declaration in node n) */
|
|
Node *c = ns;
|
|
Node *unodes = 0, *last_unodes = 0;
|
|
int ccount = 0;
|
|
|
|
while (c) {
|
|
String *cnodetype = nodeType(c);
|
|
if (Equal(cnodetype, "cdecl")) {
|
|
add_member_for_using_declaration(c, n, ccount, unodes, last_unodes);
|
|
} else if (Equal(cnodetype, "constructor")) {
|
|
add_member_for_using_declaration(c, n, ccount, unodes, last_unodes);
|
|
} else if (Equal(cnodetype, "template")) {
|
|
// A templated member (in a non-template class or in a template class that where the member has a separate template declaration)
|
|
// Find the template instantiations in the using declaration (base class)
|
|
for (Node *member = ns; member; member = nextSibling(member)) {
|
|
/* Constructors have already been handled, only add member functions
|
|
* This adds an implicit template instantiation and is a bit unusual as SWIG requires explicit %template for other template instantiations.
|
|
* However, of note, is that there is no valid C++ syntax for a template instantiation to introduce a name via a using declaration...
|
|
*
|
|
* struct Base { template <typename T> void template_method(T, T) {} };
|
|
* struct Derived : Base { using Base::template_method; };
|
|
* %template() Base::template_method<int>; // SWIG template instantiation
|
|
* template void Base::template_method<int>(int, int); // C++ template instantiation
|
|
* template void Derived::template_method<int>(int, int); // Not valid C++
|
|
*/
|
|
if (Getattr(member, "template") == ns && checkAttribute(ns, "templatetype", "cdecl")) {
|
|
if (!GetFlag(member, "feature:ignore") && !Getattr(member, "error")) {
|
|
add_member_for_using_declaration(member, n, ccount, unodes, last_unodes);
|
|
}
|
|
}
|
|
}
|
|
} else if (Equal(cnodetype, "using")) {
|
|
for (Node *member = firstChild(c); member; member = nextSibling(member)) {
|
|
add_member_for_using_declaration(member, n, ccount, unodes, last_unodes);
|
|
}
|
|
}
|
|
c = Getattr(c, "csym:nextSibling");
|
|
}
|
|
if (unodes) {
|
|
set_firstChild(n, unodes);
|
|
if (ccount > 1) {
|
|
if (!Getattr(n, "sym:overloaded")) {
|
|
Setattr(n, "sym:overloaded", n);
|
|
Setattr(n, "sym:overname", "_SWIG_0");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Hack the parse tree symbol table for overloaded methods. Replace the "using" node with the
|
|
* list of overloaded methods we have just added in as child nodes to the "using" node.
|
|
* The node will still exist, it is just the symbol table linked list of overloaded methods
|
|
* which is hacked. */
|
|
if (Getattr(n, "sym:overloaded")) {
|
|
int cnt = 0;
|
|
Node *ps = Getattr(n, "sym:previousSibling");
|
|
Node *ns = Getattr(n, "sym:nextSibling");
|
|
Node *fc = firstChild(n);
|
|
Node *firstoverloaded = Getattr(n, "sym:overloaded");
|
|
#ifdef DEBUG_OVERLOADED
|
|
show_overloaded(firstoverloaded);
|
|
#endif
|
|
|
|
if (firstoverloaded == n) {
|
|
// This 'using' node we are cutting out was the first node in the overloaded list.
|
|
// Change the first node in the list
|
|
Delattr(firstoverloaded, "sym:overloaded");
|
|
firstoverloaded = fc ? fc : ns;
|
|
|
|
// Correct all the sibling overloaded methods (before adding in new methods)
|
|
Node *nnn = ns;
|
|
while (nnn) {
|
|
Setattr(nnn, "sym:overloaded", firstoverloaded);
|
|
nnn = Getattr(nnn, "sym:nextSibling");
|
|
}
|
|
}
|
|
|
|
if (!fc) {
|
|
// Remove from overloaded list ('using' node does not actually end up adding in any methods)
|
|
if (ps) {
|
|
Setattr(ps, "sym:nextSibling", ns);
|
|
}
|
|
if (ns) {
|
|
Setattr(ns, "sym:previousSibling", ps);
|
|
}
|
|
} else {
|
|
// The 'using' node results in methods being added in - slot in these methods here
|
|
Node *pp = fc;
|
|
while (pp) {
|
|
Node *ppn = Getattr(pp, "sym:nextSibling");
|
|
Setattr(pp, "sym:overloaded", firstoverloaded);
|
|
Setattr(pp, "sym:overname", NewStringf("%s_%d", Getattr(n, "sym:overname"), cnt++));
|
|
if (ppn)
|
|
pp = ppn;
|
|
else
|
|
break;
|
|
}
|
|
if (ps) {
|
|
Setattr(ps, "sym:nextSibling", fc);
|
|
Setattr(fc, "sym:previousSibling", ps);
|
|
}
|
|
if (ns) {
|
|
Setattr(ns, "sym:previousSibling", pp);
|
|
Setattr(pp, "sym:nextSibling", ns);
|
|
}
|
|
}
|
|
Delattr(n, "sym:previousSibling");
|
|
Delattr(n, "sym:nextSibling");
|
|
Delattr(n, "sym:overloaded");
|
|
Delattr(n, "sym:overname");
|
|
clean_overloaded(firstoverloaded);
|
|
#ifdef DEBUG_OVERLOADED
|
|
show_overloaded(firstoverloaded);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Node *c = 0;
|
|
for (c = firstChild(n); c; c = nextSibling(c)) {
|
|
if (Equal(nodeType(c), "cdecl")) {
|
|
process_exceptions(c);
|
|
|
|
if (inclass)
|
|
class_member_is_defined_in_bases(c, inclass);
|
|
} else if (Equal(nodeType(c), "constructor")) {
|
|
constructorDeclaration(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
return SWIG_OK;
|
|
}
|
|
|
|
virtual int cDeclaration(Node *n) {
|
|
|
|
process_exceptions(n);
|
|
|
|
if (inclass) {
|
|
/* check whether the member node n is defined in class node in class's bases */
|
|
class_member_is_defined_in_bases(n, inclass);
|
|
|
|
/* Check to see if this is a static member or not. If so, we add an attribute
|
|
cplus:staticbase that saves the current class */
|
|
|
|
int is_static = Swig_storage_isstatic(n);
|
|
if (is_static) {
|
|
Setattr(n, "cplus:staticbase", inclass);
|
|
}
|
|
|
|
if (Cmp(Getattr(n, "kind"), "variable") == 0) {
|
|
/* Check member variable to determine whether assignment is valid */
|
|
bool is_reference;
|
|
bool is_const;
|
|
bool assignable = is_assignable(n, is_reference, is_const);
|
|
if (!assignable || is_const) {
|
|
SetFlag(n, "feature:immutable");
|
|
}
|
|
if (!is_static) {
|
|
if (!assignable || is_reference || is_const)
|
|
SetFlag(inclass, "allocate:has_nonassignable"); // The class has a variable that cannot be assigned to
|
|
}
|
|
}
|
|
|
|
String *name = Getattr(n, "name");
|
|
if (cplus_mode != PUBLIC) {
|
|
if (Strcmp(name, "operator =") == 0) {
|
|
/* Look for a private assignment operator */
|
|
if (!GetFlag(n, "deleted"))
|
|
Setattr(inclass, "allocate:has_assign", "1");
|
|
Setattr(inclass, "allocate:noassign", "1");
|
|
} else if (Strcmp(name, "operator new") == 0) {
|
|
/* Look for a private new operator */
|
|
if (!GetFlag(n, "deleted"))
|
|
Setattr(inclass, "allocate:has_new", "1");
|
|
Setattr(inclass, "allocate:nonew", "1");
|
|
}
|
|
} else {
|
|
if (Strcmp(name, "operator =") == 0) {
|
|
if (!GetFlag(n, "deleted"))
|
|
Setattr(inclass, "allocate:has_assign", "1");
|
|
else
|
|
Setattr(inclass, "allocate:noassign", "1");
|
|
} else if (Strcmp(name, "operator new") == 0) {
|
|
if (!GetFlag(n, "deleted"))
|
|
Setattr(inclass, "allocate:has_new", "1");
|
|
else
|
|
Setattr(inclass, "allocate:nonew", "1");
|
|
}
|
|
/* Look for smart pointer operator */
|
|
if ((Strcmp(name, "operator ->") == 0) && (!GetFlag(n, "feature:ignore"))) {
|
|
/* Look for version with no parameters */
|
|
Node *sn = n;
|
|
while (sn) {
|
|
if (!Getattr(sn, "parms")) {
|
|
SwigType *type = SwigType_typedef_resolve_all(Getattr(sn, "type"));
|
|
SwigType_push(type, Getattr(sn, "decl"));
|
|
Delete(SwigType_pop_function(type));
|
|
SwigType *base = SwigType_base(type);
|
|
Node *sc = Swig_symbol_clookup(base, 0);
|
|
if ((sc) && (Strcmp(nodeType(sc), "class") == 0)) {
|
|
if (SwigType_check_decl(type, "p.")) {
|
|
/* Need to check if type is a const pointer */
|
|
int isconst = 0;
|
|
Delete(SwigType_pop(type));
|
|
if (SwigType_isconst(type)) {
|
|
isconst = !Getattr(inclass, "allocate:smartpointermutable");
|
|
Setattr(inclass, "allocate:smartpointerconst", "1");
|
|
}
|
|
else {
|
|
Setattr(inclass, "allocate:smartpointermutable", "1");
|
|
}
|
|
List *methods = smart_pointer_methods(sc, 0, isconst);
|
|
Setattr(inclass, "allocate:smartpointer", methods);
|
|
Setattr(inclass, "allocate:smartpointerpointeeclassname", Getattr(sc, "name"));
|
|
} else {
|
|
/* Hmmm. The return value is not a pointer. If the type is a value
|
|
or reference. We're going to chase it to see if another operator->()
|
|
can be found */
|
|
if ((SwigType_check_decl(type, "")) || (SwigType_check_decl(type, "r."))) {
|
|
Node *nn = Swig_symbol_clookup("operator ->", Getattr(sc, "symtab"));
|
|
if (nn) {
|
|
Delete(base);
|
|
Delete(type);
|
|
sn = nn;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Delete(base);
|
|
Delete(type);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (Cmp(Getattr(n, "kind"), "variable") == 0) {
|
|
bool is_reference;
|
|
bool is_const;
|
|
bool assignable = is_assignable(n, is_reference, is_const);
|
|
if (!assignable || is_const) {
|
|
SetFlag(n, "feature:immutable");
|
|
}
|
|
}
|
|
}
|
|
return SWIG_OK;
|
|
}
|
|
|
|
virtual int templateDeclaration(Node *n) {
|
|
String *ttype = Getattr(n, "templatetype");
|
|
if (Equal(ttype, "constructor")) {
|
|
// Templated constructors need to be taken account of even if not instantiated with %template
|
|
constructorDeclaration(n);
|
|
}
|
|
return SWIG_OK;
|
|
}
|
|
|
|
virtual int constructorDeclaration(Node *n) {
|
|
if (!inclass)
|
|
return SWIG_OK;
|
|
|
|
Parm *parms = Getattr(n, "parms");
|
|
bool deleted_constructor = (GetFlag(n, "deleted"));
|
|
bool default_constructor = !ParmList_numrequired(parms);
|
|
AccessMode access_mode = accessModeFromString(Getattr(n, "access"));
|
|
process_exceptions(n);
|
|
|
|
if (!deleted_constructor) {
|
|
if (!extendmode) {
|
|
if (default_constructor) {
|
|
/* Class does define a default constructor */
|
|
/* However, we had better see where it is defined */
|
|
if (access_mode == PUBLIC) {
|
|
Setattr(inclass, "allocate:default_constructor", "1");
|
|
} else if (access_mode == PROTECTED) {
|
|
Setattr(inclass, "allocate:default_base_constructor", "1");
|
|
}
|
|
}
|
|
/* Class defines some kind of constructor. May or may not be public */
|
|
Setattr(inclass, "allocate:has_constructor", "1");
|
|
if (access_mode == PUBLIC) {
|
|
Setattr(inclass, "allocate:public_constructor", "1");
|
|
}
|
|
} else {
|
|
Setattr(inclass, "allocate:has_constructor", "1");
|
|
Setattr(inclass, "allocate:public_constructor", "1");
|
|
}
|
|
} else {
|
|
if (default_constructor && !extendmode)
|
|
SetFlag(inclass, "allocate:deleted_default_constructor");
|
|
}
|
|
|
|
/* See if this is a copy constructor */
|
|
if (parms && (ParmList_numrequired(parms) == 1)) {
|
|
/* Look for a few cases. X(const X &), X(X &), X(X *) */
|
|
int copy_constructor = 0;
|
|
int copy_constructor_non_const = 0;
|
|
SwigType *type = Getattr(inclass, "name");
|
|
String *tn = NewStringf("r.q(const).%s", type);
|
|
String *cc = SwigType_typedef_resolve_all(tn);
|
|
SwigType *rt = SwigType_typedef_resolve_all(Getattr(parms, "type"));
|
|
if (SwigType_istemplate(type)) {
|
|
String *tmp = Swig_symbol_template_deftype(cc, 0);
|
|
Delete(cc);
|
|
cc = tmp;
|
|
tmp = Swig_symbol_template_deftype(rt, 0);
|
|
Delete(rt);
|
|
rt = tmp;
|
|
}
|
|
if (Strcmp(cc, rt) == 0) {
|
|
copy_constructor = 1;
|
|
} else {
|
|
Delete(cc);
|
|
cc = NewStringf("r.%s", Getattr(inclass, "name"));
|
|
if (Strcmp(cc, Getattr(parms, "type")) == 0) {
|
|
copy_constructor = 1;
|
|
copy_constructor_non_const = 1;
|
|
} else {
|
|
Delete(cc);
|
|
cc = NewStringf("p.%s", Getattr(inclass, "name"));
|
|
String *ty = SwigType_strip_qualifiers(Getattr(parms, "type"));
|
|
if (Strcmp(cc, ty) == 0) {
|
|
copy_constructor = 1;
|
|
}
|
|
Delete(ty);
|
|
}
|
|
}
|
|
Delete(cc);
|
|
Delete(rt);
|
|
Delete(tn);
|
|
|
|
if (copy_constructor) {
|
|
if (!deleted_constructor) {
|
|
Setattr(n, "copy_constructor", "1");
|
|
Setattr(inclass, "allocate:has_copy_constructor", "1");
|
|
if (access_mode == PUBLIC) {
|
|
Setattr(inclass, "allocate:copy_constructor", "1");
|
|
} else if (access_mode == PROTECTED) {
|
|
Setattr(inclass, "allocate:copy_base_constructor", "1");
|
|
}
|
|
if (copy_constructor_non_const) {
|
|
Setattr(n, "copy_constructor_non_const", "1");
|
|
Setattr(inclass, "allocate:has_copy_constructor_non_const", "1");
|
|
if (access_mode == PUBLIC) {
|
|
Setattr(inclass, "allocate:copy_constructor_non_const", "1");
|
|
} else if (access_mode == PROTECTED) {
|
|
Setattr(inclass, "allocate:copy_base_constructor_non_const", "1");
|
|
}
|
|
}
|
|
} else {
|
|
if (!extendmode)
|
|
SetFlag(inclass, "allocate:deleted_copy_constructor");
|
|
}
|
|
}
|
|
}
|
|
return SWIG_OK;
|
|
}
|
|
|
|
virtual int destructorDeclaration(Node *n) {
|
|
(void) n;
|
|
if (!inclass)
|
|
return SWIG_OK;
|
|
|
|
if (!GetFlag(n, "deleted")) {
|
|
if (!extendmode) {
|
|
Setattr(inclass, "allocate:has_destructor", "1");
|
|
if (cplus_mode == PUBLIC) {
|
|
Setattr(inclass, "allocate:default_destructor", "1");
|
|
} else if (cplus_mode == PROTECTED) {
|
|
Setattr(inclass, "allocate:default_base_destructor", "1");
|
|
} else if (cplus_mode == PRIVATE) {
|
|
Setattr(inclass, "allocate:private_destructor", "1");
|
|
}
|
|
} else {
|
|
Setattr(inclass, "allocate:has_destructor", "1");
|
|
Setattr(inclass, "allocate:default_destructor", "1");
|
|
}
|
|
} else {
|
|
if (!extendmode)
|
|
SetFlag(inclass, "allocate:deleted_default_destructor");
|
|
}
|
|
|
|
return SWIG_OK;
|
|
}
|
|
|
|
static void addCopyConstructor(Node *n) {
|
|
Node *cn = NewHash();
|
|
set_nodeType(cn, "constructor");
|
|
Setattr(cn, "access", "public");
|
|
Setfile(cn, Getfile(n));
|
|
Setline(cn, Getline(n));
|
|
|
|
int copy_constructor_non_const = GetFlag(n, "allocate:copy_constructor_non_const");
|
|
String *cname = Getattr(n, "name");
|
|
SwigType *type = Copy(cname);
|
|
String *lastname = Swig_scopename_last(cname);
|
|
String *name = SwigType_templateprefix(lastname);
|
|
String *cc = NewStringf(copy_constructor_non_const ? "r.%s" : "r.q(const).%s", type);
|
|
String *decl = NewStringf("f(%s).", cc);
|
|
String *oldname = Getattr(n, "sym:name");
|
|
|
|
if (Getattr(n, "allocate:has_constructor")) {
|
|
// to work properly with '%rename Class', we must look
|
|
// for any other constructor in the class, which has not been
|
|
// renamed, and use its name as oldname.
|
|
Node *c;
|
|
for (c = firstChild(n); c; c = nextSibling(c)) {
|
|
if (Equal(nodeType(c), "constructor")) {
|
|
String *csname = Getattr(c, "sym:name");
|
|
String *clast = Swig_scopename_last(Getattr(c, "name"));
|
|
if (Equal(csname, clast)) {
|
|
oldname = csname;
|
|
Delete(clast);
|
|
break;
|
|
}
|
|
Delete(clast);
|
|
}
|
|
}
|
|
}
|
|
|
|
String *symname = Swig_name_make(cn, cname, name, decl, oldname);
|
|
if (Strcmp(symname, "$ignore") != 0) {
|
|
Parm *p = NewParm(cc, "other", n);
|
|
|
|
Setattr(cn, "name", name);
|
|
Setattr(cn, "sym:name", symname);
|
|
SetFlag(cn, "feature:new");
|
|
Setattr(cn, "decl", decl);
|
|
Setattr(cn, "ismember", "1");
|
|
Setattr(cn, "parentNode", n);
|
|
Setattr(cn, "parms", p);
|
|
Setattr(cn, "copy_constructor", "1");
|
|
|
|
Symtab *oldscope = Swig_symbol_setscope(Getattr(n, "symtab"));
|
|
Node *on = Swig_symbol_add(symname, cn);
|
|
Swig_features_get(Swig_cparse_features(), Swig_symbol_qualifiedscopename(0), name, decl, cn);
|
|
Swig_symbol_setscope(oldscope);
|
|
|
|
if (on == cn) {
|
|
Node *access = NewHash();
|
|
set_nodeType(access, "access");
|
|
Setattr(access, "kind", "public");
|
|
appendChild(n, access);
|
|
appendChild(n, cn);
|
|
Setattr(n, "has_copy_constructor", "1");
|
|
Setattr(n, "copy_constructor_decl", decl);
|
|
Setattr(n, "allocate:copy_constructor", "1");
|
|
Delete(access);
|
|
}
|
|
}
|
|
Delete(cn);
|
|
Delete(lastname);
|
|
Delete(name);
|
|
Delete(decl);
|
|
Delete(symname);
|
|
}
|
|
|
|
static void addDefaultConstructor(Node *n) {
|
|
Node *cn = NewHash();
|
|
set_nodeType(cn, "constructor");
|
|
Setattr(cn, "access", "public");
|
|
Setfile(cn, Getfile(n));
|
|
Setline(cn, Getline(n));
|
|
|
|
String *cname = Getattr(n, "name");
|
|
String *lastname = Swig_scopename_last(cname);
|
|
String *name = SwigType_templateprefix(lastname);
|
|
String *decl = NewString("f().");
|
|
String *oldname = Getattr(n, "sym:name");
|
|
String *symname = Swig_name_make(cn, cname, name, decl, oldname);
|
|
|
|
if (Strcmp(symname, "$ignore") != 0) {
|
|
Setattr(cn, "name", name);
|
|
Setattr(cn, "sym:name", symname);
|
|
SetFlag(cn, "feature:new");
|
|
Setattr(cn, "decl", decl);
|
|
Setattr(cn, "ismember", "1");
|
|
Setattr(cn, "parentNode", n);
|
|
Setattr(cn, "default_constructor", "1");
|
|
|
|
Symtab *oldscope = Swig_symbol_setscope(Getattr(n, "symtab"));
|
|
Node *on = Swig_symbol_add(symname, cn);
|
|
Swig_features_get(Swig_cparse_features(), Swig_symbol_qualifiedscopename(0), name, decl, cn);
|
|
Swig_symbol_setscope(oldscope);
|
|
|
|
if (on == cn) {
|
|
Node *access = NewHash();
|
|
set_nodeType(access, "access");
|
|
Setattr(access, "kind", "public");
|
|
appendChild(n, access);
|
|
appendChild(n, cn);
|
|
Setattr(n, "has_default_constructor", "1");
|
|
Setattr(n, "allocate:default_constructor", "1");
|
|
Delete(access);
|
|
}
|
|
}
|
|
Delete(cn);
|
|
Delete(lastname);
|
|
Delete(name);
|
|
Delete(decl);
|
|
Delete(symname);
|
|
}
|
|
|
|
static void addDestructor(Node *n) {
|
|
Node *cn = NewHash();
|
|
set_nodeType(cn, "destructor");
|
|
Setattr(cn, "access", "public");
|
|
Setfile(cn, Getfile(n));
|
|
Setline(cn, Getline(n));
|
|
|
|
String *cname = Getattr(n, "name");
|
|
String *lastname = Swig_scopename_last(cname);
|
|
String *name = SwigType_templateprefix(lastname);
|
|
Insert(name, 0, "~");
|
|
String *decl = NewString("f().");
|
|
String *symname = Swig_name_make(cn, cname, name, decl, 0);
|
|
if (Strcmp(symname, "$ignore") != 0) {
|
|
String *possible_nonstandard_symname = NewStringf("~%s", Getattr(n, "sym:name"));
|
|
|
|
Setattr(cn, "name", name);
|
|
Setattr(cn, "sym:name", symname);
|
|
Setattr(cn, "decl", "f().");
|
|
Setattr(cn, "ismember", "1");
|
|
Setattr(cn, "parentNode", n);
|
|
|
|
Symtab *oldscope = Swig_symbol_setscope(Getattr(n, "symtab"));
|
|
Node *nonstandard_destructor = Equal(possible_nonstandard_symname, symname) ? 0 : Swig_symbol_clookup(possible_nonstandard_symname, 0);
|
|
Node *on = Swig_symbol_add(symname, cn);
|
|
Swig_features_get(Swig_cparse_features(), Swig_symbol_qualifiedscopename(0), name, decl, cn);
|
|
Swig_symbol_setscope(oldscope);
|
|
|
|
if (on == cn) {
|
|
// SWIG accepts a non-standard named destructor in %extend that uses a typedef for the destructor name
|
|
// For example: typedef struct X {} XX; %extend X { ~XX() {...} }
|
|
// Don't add another destructor if a nonstandard one has been declared
|
|
if (!nonstandard_destructor) {
|
|
Node *access = NewHash();
|
|
set_nodeType(access, "access");
|
|
Setattr(access, "kind", "public");
|
|
appendChild(n, access);
|
|
appendChild(n, cn);
|
|
Setattr(n, "has_destructor", "1");
|
|
Setattr(n, "allocate:has_destructor", "1");
|
|
Delete(access);
|
|
}
|
|
}
|
|
Delete(possible_nonstandard_symname);
|
|
}
|
|
Delete(cn);
|
|
Delete(lastname);
|
|
Delete(name);
|
|
Delete(decl);
|
|
Delete(symname);
|
|
}
|
|
|
|
};
|
|
|
|
void Swig_default_allocators(Node *n) {
|
|
if (!n)
|
|
return;
|
|
Allocate *a = new Allocate;
|
|
a->top(n);
|
|
delete a;
|
|
}
|