| /* ----------------------------------------------------------------------------- |
| * 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. |
| * |
| * 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" |
| #include "cparse.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 (List *bases = Getattr(n, "interface:bases")) { |
| for (Iterator base = First(bases); base.item; base = Next(base)) { |
| Node *cls = 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 |
| if (!checkAttribute(child, "kind", "function")) |
| continue; |
| if (checkAttribute(child, "storage", "static")) |
| continue; // accept virtual methods, non-virtual methods too... mmm??. Warn that the interface class has something that is not a virtual method? |
| Node *nn = copyNode(child); |
| Setattr(nn, "interface:owner", cls); |
| ParmList *parms = CopyParmList(Getattr(child, "parms")); |
| Setattr(nn, "parms", parms); |
| Delete(parms); |
| ParmList *throw_parm_list = Getattr(child, "throws"); |
| if (throw_parm_list) |
| Setattr(nn, "throws", CopyParmList(throw_parm_list)); |
| Append(methods, nn); |
| } |
| } |
| } |
| } |
| return methods; |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * collect_interface_bases |
| * ----------------------------------------------------------------------------- */ |
| |
| static void collect_interface_bases(List *bases, Node *n) { |
| if (GetFlag(n, "feature:interface")) { |
| String *name = Getattr(n, "interface:name"); |
| if (!Getattr(bases, name)) |
| Append(bases, n); |
| } |
| |
| if (List *baselist = Getattr(n, "bases")) { |
| for (Iterator base = First(baselist); base.item; base = Next(base)) { |
| if (!GetFlag(base.item, "feature:ignore")) { |
| if (GetFlag(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 (GetFlag(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 (!GetFlag(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"))); |
| Exit(EXIT_FAILURE); |
| } |
| } |
| } |
| } |
| } |
| |
| List *interface_bases = NewList(); |
| 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 (GetFlag(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"))); |
| 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 = GetFlag(n, "feature:interface") ? true : false; |
| 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) { |
| // Add method copied from base class to this derived class |
| Node *cn = mi.item; |
| Delattr(cn, "sym:overname"); |
| String *prefix = Getattr(n, "name"); |
| String *name = Getattr(cn, "name"); |
| String *decl = Getattr(cn, "decl"); |
| String *oldname = Getattr(cn, "sym:name"); |
| |
| String *symname = Swig_name_make(cn, prefix, name, decl, oldname); |
| if (Strcmp(symname, "$ignore") != 0) { |
| Symtab *oldscope = Swig_symbol_setscope(Getattr(n, "symtab")); |
| Node *on = Swig_symbol_add(symname, cn); |
| (void)on; |
| assert(on == cn); |
| |
| // Features from the copied base class method are already present, now add in features specific to the added method in the derived class |
| Swig_features_get(Swig_cparse_features(), Swig_symbol_qualifiedscopename(0), name, decl, cn); |
| Swig_symbol_setscope(oldscope); |
| appendChild(n, cn); |
| } |
| } else { |
| Delete(mi.item); |
| } |
| } |
| Delete(methods); |
| } |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * Swig_interface_feature_enable() |
| * |
| * Turn on interface feature support |
| * ----------------------------------------------------------------------------- */ |
| |
| void Swig_interface_feature_enable() { |
| interface_feature_enabled = true; |
| } |