diff --git a/Source/Makefile.am b/Source/Makefile.am index 66a7addfd..b04e3542f 100644 --- a/Source/Makefile.am +++ b/Source/Makefile.am @@ -47,6 +47,7 @@ eswig_SOURCES = CParse/cscanner.c \ Modules/emit.cxx \ Modules/go.cxx \ Modules/guile.cxx \ + Modules/interface.cxx \ Modules/java.cxx \ Modules/javascript.cxx \ Modules/lang.cxx \ diff --git a/Source/Modules/allocate.cxx b/Source/Modules/allocate.cxx index f79373d18..3d382b378 100644 --- a/Source/Modules/allocate.cxx +++ b/Source/Modules/allocate.cxx @@ -729,6 +729,8 @@ Allocate(): } } + Swig_interface_propagate_methods(n); + /* Only care about default behavior. Remove temporary values */ Setattr(n, "allocate:visit", "1"); Swig_symbol_setscope(symtab); diff --git a/Source/Modules/csharp.cxx b/Source/Modules/csharp.cxx index 4170704f8..e09332942 100644 --- a/Source/Modules/csharp.cxx +++ b/Source/Modules/csharp.cxx @@ -18,8 +18,6 @@ /* Hash type used for upcalls from C/C++ */ typedef DOH UpcallData; -// helper function used in feature:interface implementation -void Swig_propagate_interface_methods(Node *n); class CSHARP:public Language { static const char *usage; @@ -276,6 +274,7 @@ public: SWIG_config_file("csharp.swg"); allow_overloading(); + Swig_interface_feature_enable(); } /* --------------------------------------------------------------------- @@ -4403,8 +4402,6 @@ public: String *old_director_delegate_instances = director_delegate_instances; String *old_director_method_types = director_method_types; String *old_director_connect_parms = director_connect_parms; - if (proxy_flag) - Swig_propagate_interface_methods(n); int ret = Language::classDeclaration(n); diff --git a/Source/Modules/interface.cxx b/Source/Modules/interface.cxx new file mode 100644 index 000000000..03d15a615 --- /dev/null +++ b/Source/Modules/interface.cxx @@ -0,0 +1,181 @@ +/* ----------------------------------------------------------------------------- + * 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 http://www.swig.org/legal.html. + * + * interface.cxx + * + * This module contains support for the interface feature. + * This feature is used in language modules where the target language does not + * naturally support C++ style multiple inheritance, but does support inheritance + * from multiple interfaces. + * ----------------------------------------------------------------------------- */ + +#include "swigmod.h" + +static bool interface_feature_enabled = false; + +/* ----------------------------------------------------------------------------- + * collect_interface_methods() + * + * Create a list of all the methods from the base classes of class n that are + * marked as an interface. The resulting list is thus the list of methods that + * need to be implemented in order for n to be non-abstract. + * ----------------------------------------------------------------------------- */ + +static List *collect_interface_methods(Node *n) { + List *methods = NewList(); + if (Hash *bases = Getattr(n, "interface:bases")) { + List *keys = Keys(bases); + for (Iterator base = First(keys); base.item; base = Next(base)) { + Node *cls = Getattr(bases, base.item); + if (cls == n) + continue; + for (Node *child = firstChild(cls); child; child = nextSibling(child)) { + if (Cmp(nodeType(child), "cdecl") == 0) { + if (GetFlag(child, "feature:ignore") || Getattr(child, "interface:owner")) + continue; // skip methods propagated to bases + Node *m = Copy(child); + set_nextSibling(m, NIL); + set_previousSibling(m, NIL); + Setattr(m, "interface:owner", cls); + Append(methods, m); + } + } + } + Delete(keys); + } + return methods; +} + +/* ----------------------------------------------------------------------------- + * collect_interface_bases + * ----------------------------------------------------------------------------- */ + +static void collect_interface_bases(Hash *bases, Node *n) { + if (Getattr(n, "feature:interface")) { + String *name = Getattr(n, "interface:name"); + if (!Getattr(bases, name)) + Setattr(bases, name, n); + } + + if (List *baselist = Getattr(n, "bases")) { + for (Iterator base = First(baselist); base.item; base = Next(base)) { + if (!GetFlag(base.item, "feature:ignore")) { + if (Getattr(base.item, "feature:interface")) + collect_interface_bases(bases, base.item); + } + } + } +} + +/* ----------------------------------------------------------------------------- + * collect_interface_base_classes() + * + * Create a hash containing all the classes up the inheritance hierarchy + * marked with feature:interface (including this class n). + * Stops going up the inheritance chain as soon as a class is found without + * feature:interface. + * The idea is to find all the base interfaces that a class must implement. + * ----------------------------------------------------------------------------- */ + +static void collect_interface_base_classes(Node *n) { + if (Getattr(n, "feature:interface")) { + // check all bases are also interfaces + if (List *baselist = Getattr(n, "bases")) { + for (Iterator base = First(baselist); base.item; base = Next(base)) { + if (!GetFlag(base.item, "feature:ignore")) { + if (!Getattr(base.item, "feature:interface")) { + Swig_error(Getfile(n), Getline(n), "Base class '%s' of '%s' is not similarly marked as an interface.\n", SwigType_namestr(Getattr(base.item, "name")), SwigType_namestr(Getattr(n, "name"))); + SWIG_exit(EXIT_FAILURE); + } + } + } + } + } + + Hash *interface_bases = NewHash(); + collect_interface_bases(interface_bases, n); + if (Len(interface_bases) == 0) + Delete(interface_bases); + else + Setattr(n, "interface:bases", interface_bases); +} + +/* ----------------------------------------------------------------------------- + * process_interface_name() + * ----------------------------------------------------------------------------- */ + +static void process_interface_name(Node *n) { + if (Getattr(n, "feature:interface")) { + String *interface_name = Getattr(n, "feature:interface:name"); + if (!Len(interface_name)) { + Swig_error(Getfile(n), Getline(n), "The interface feature for '%s' is missing the name attribute.\n", SwigType_namestr(Getattr(n, "name"))); + SWIG_exit(EXIT_FAILURE); + } + if (Strchr(interface_name, '%')) { + String *name = NewStringf(interface_name, Getattr(n, "sym:name")); + Setattr(n, "interface:name", name); + } else { + Setattr(n, "interface:name", interface_name); + } + } +} + +/* ----------------------------------------------------------------------------- + * Swig_interface_propagate_methods() + * + * Find all the base classes marked as an interface (with feature:interface) for + * class node n. For each of these, add all of its methods as methods of n so that + * n is not abstract. If class n is also marked as an interface, it will remain + * abstract and not have any methods added. + * ----------------------------------------------------------------------------- */ + +void Swig_interface_propagate_methods(Node *n) { + if (interface_feature_enabled) { + process_interface_name(n); + collect_interface_base_classes(n); + List *methods = collect_interface_methods(n); + bool is_interface = Getattr(n, "feature:interface") != 0; + for (Iterator mi = First(methods); mi.item; mi = Next(mi)) { + if (!is_interface && GetFlag(mi.item, "abstract")) + continue; + String *this_decl = Getattr(mi.item, "decl"); + String *resolved_decl = SwigType_typedef_resolve_all(this_decl); + bool overloaded = false; + if (SwigType_isfunction(resolved_decl)) { + String *name = Getattr(mi.item, "name"); + for (Node *child = firstChild(n); child; child = nextSibling(child)) { + if (Getattr(child, "interface:owner")) + break; // at the end of the list are newly appended methods + if (checkAttribute(child, "name", name)) { + String *decl = SwigType_typedef_resolve_all(Getattr(child, "decl")); + overloaded = Strcmp(decl, this_decl) == 0; + Delete(decl); + if (overloaded) + break; + } + } + } + Delete(resolved_decl); + if (!overloaded) + appendChild(n, mi.item); + else + Delete(mi.item); + } + Delete(methods); + } +} + +/* ----------------------------------------------------------------------------- + * Swig_interface_feature_enable() + * + * Turn on interface feature support + * ----------------------------------------------------------------------------- */ + +void Swig_interface_feature_enable() { + interface_feature_enabled = true; +} diff --git a/Source/Modules/java.cxx b/Source/Modules/java.cxx index 9f0b83329..d672041c7 100644 --- a/Source/Modules/java.cxx +++ b/Source/Modules/java.cxx @@ -18,8 +18,6 @@ /* Hash type used for upcalls from C/C++ */ typedef DOH UpcallData; -// helper function used in feature:interface implementation -void Swig_propagate_interface_methods(Node *n); class JAVA:public Language { static const char *usage; @@ -307,6 +305,7 @@ public: SWIG_config_file("java.swg"); allow_overloading(); + Swig_interface_feature_enable(); } /* --------------------------------------------------------------------- @@ -2076,8 +2075,6 @@ public: * ---------------------------------------------------------------------- */ int classDeclaration(Node *n) { - if (proxy_flag) - Swig_propagate_interface_methods(n); return Language::classDeclaration(n); } diff --git a/Source/Modules/lang.cxx b/Source/Modules/lang.cxx index ceebef95d..e3ff5f302 100644 --- a/Source/Modules/lang.cxx +++ b/Source/Modules/lang.cxx @@ -3818,152 +3818,3 @@ Hash *Language::getClassHash() const { return classhash; } -/* ----------------------------------------------------------------------------- - * collect_interface_methods() - * - * Create a list of all the methods from the base classes of class n that are - * marked as an interface. The resulting list is thus the list of methods that - * need to be implemented in order for n to be non-abstract. - * ----------------------------------------------------------------------------- */ - -static List *collect_interface_methods(Node *n) { - List *methods = NewList(); - if (Hash *bases = Getattr(n, "interface:bases")) { - List *keys = Keys(bases); - for (Iterator base = First(keys); base.item; base = Next(base)) { - Node *cls = Getattr(bases, base.item); - if (cls == n) - continue; - for (Node *child = firstChild(cls); child; child = nextSibling(child)) { - if (Cmp(nodeType(child), "cdecl") == 0) { - if (GetFlag(child, "feature:ignore") || Getattr(child, "interface:owner")) - continue; // skip methods propagated to bases - Node *m = Copy(child); - set_nextSibling(m, NIL); - set_previousSibling(m, NIL); - Setattr(m, "interface:owner", cls); - Append(methods, m); - } - } - } - Delete(keys); - } - return methods; -} - -/* ----------------------------------------------------------------------------- - * collect_interface_bases - * ----------------------------------------------------------------------------- */ - -static void collect_interface_bases(Hash *bases, Node *n) { - if (Getattr(n, "feature:interface")) { - String *name = Getattr(n, "interface:name"); - if (!Getattr(bases, name)) - Setattr(bases, name, n); - } - - if (List *baselist = Getattr(n, "bases")) { - for (Iterator base = First(baselist); base.item; base = Next(base)) { - if (!GetFlag(base.item, "feature:ignore")) { - if (Getattr(base.item, "feature:interface")) - collect_interface_bases(bases, base.item); - } - } - } -} - -/* ----------------------------------------------------------------------------- - * collect_interface_base_classes() - * - * Create a hash containing all the classes up the inheritance hierarchy - * marked with feature:interface (including this class n). - * Stops going up the inheritance chain as soon as a class is found without - * feature:interface. - * The idea is to find all the base interfaces that a class must implement. - * ----------------------------------------------------------------------------- */ - -static void collect_interface_base_classes(Node *n) { - if (Getattr(n, "feature:interface")) { - // check all bases are also interfaces - if (List *baselist = Getattr(n, "bases")) { - for (Iterator base = First(baselist); base.item; base = Next(base)) { - if (!GetFlag(base.item, "feature:ignore")) { - if (!Getattr(base.item, "feature:interface")) { - Swig_error(Getfile(n), Getline(n), "Base class '%s' of '%s' is not similarly marked as an interface.\n", SwigType_namestr(Getattr(base.item, "name")), SwigType_namestr(Getattr(n, "name"))); - SWIG_exit(EXIT_FAILURE); - } - } - } - } - } - - Hash *interface_bases = NewHash(); - collect_interface_bases(interface_bases, n); - if (Len(interface_bases) == 0) - Delete(interface_bases); - else - Setattr(n, "interface:bases", interface_bases); -} - -/* ----------------------------------------------------------------------------- - * process_interface_name() - * ----------------------------------------------------------------------------- */ - -void process_interface_name(Node *n) { - if (Getattr(n, "feature:interface")) { - String *interface_name = Getattr(n, "feature:interface:name"); - if (!Len(interface_name)) { - Swig_error(Getfile(n), Getline(n), "The interface feature for '%s' is missing the name attribute.\n", SwigType_namestr(Getattr(n, "name"))); - SWIG_exit(EXIT_FAILURE); - } - if (Strchr(interface_name, '%')) { - String *name = NewStringf(interface_name, Getattr(n, "sym:name")); - Setattr(n, "interface:name", name); - } else { - Setattr(n, "interface:name", interface_name); - } - } -} - -/* ----------------------------------------------------------------------------- - * Swig_propagate_interface_methods() - * - * Find all the base classes marked as an interface (with feature:interface) for - * class node n. For each of these, add all of its methods as methods of n so that - * n is not abstract. If class n is also marked as an interface, it will remain - * abstract and not have any methods added. - * ----------------------------------------------------------------------------- */ - -void Swig_propagate_interface_methods(Node *n) { - process_interface_name(n); - collect_interface_base_classes(n); - List *methods = collect_interface_methods(n); - bool is_interface = Getattr(n, "feature:interface") != 0; - for (Iterator mi = First(methods); mi.item; mi = Next(mi)) { - if (!is_interface && GetFlag(mi.item, "abstract")) - continue; - String *this_decl = Getattr(mi.item, "decl"); - String *resolved_decl = SwigType_typedef_resolve_all(this_decl); - bool overloaded = false; - if (SwigType_isfunction(resolved_decl)) { - String *name = Getattr(mi.item, "name"); - for (Node *child = firstChild(n); child; child = nextSibling(child)) { - if (Getattr(child, "interface:owner")) - break; // at the end of the list are newly appended methods - if (checkAttribute(child, "name", name)) { - String *decl = SwigType_typedef_resolve_all(Getattr(child, "decl")); - overloaded = Strcmp(decl, this_decl) == 0; - Delete(decl); - if (overloaded) - break; - } - } - } - Delete(resolved_decl); - if (!overloaded) - appendChild(n, mi.item); - else - Delete(mi.item); - } - Delete(methods); -} diff --git a/Source/Modules/swigmod.h b/Source/Modules/swigmod.h index c4007be51..4f4e7a117 100644 --- a/Source/Modules/swigmod.h +++ b/Source/Modules/swigmod.h @@ -422,19 +422,24 @@ extern "C" { } /* Contracts */ - void Swig_contracts(Node *n); void Swig_contract_mode_set(int flag); int Swig_contract_mode_get(); /* Browser */ - void Swig_browser(Node *n, int); void Swig_default_allocators(Node *n); void Swig_process_types(Node *n); + +/* Nested classes */ void Swig_nested_process_classes(Node *n); void Swig_nested_name_unnamed_c_structs(Node *n); +/* Interface feature */ +void Swig_interface_feature_enable(); +void Swig_interface_propagate_methods(Node *n); + +/* Miscellaneous */ template class save_value { T _value; T& _value_ptr;