| /* ----------------------------------------------------------------------------- |
| * 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 *this_decl_resolved = SwigType_typedef_resolve_all(this_decl); |
| bool identically_overloaded_method = false; // true when a base class' method is implemented in n |
| if (SwigType_isfunction(this_decl_resolved)) { |
| 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 (Cmp(nodeType(child), "cdecl") == 0) { |
| if (checkAttribute(child, "name", name)) { |
| String *decl = SwigType_typedef_resolve_all(Getattr(child, "decl")); |
| identically_overloaded_method = Strcmp(decl, this_decl_resolved) == 0; |
| Delete(decl); |
| if (identically_overloaded_method) |
| break; |
| } |
| } |
| } |
| } |
| Delete(this_decl_resolved); |
| if (!identically_overloaded_method) { |
| // TODO: Fix if the method is overloaded with different arguments / has default args |
| 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; |
| } |