| /* ----------------------------------------------------------------------------- | |
| * 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")) { | |
| if (!Swig_item_in_list(bases, n)) | |
| 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 list 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. | |
| * Remove duplicate bases (in the event of multiple inheritance). | |
| * 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; | |
| } |