mirror of https://github.com/swig/swig
210 lines
8.2 KiB
C++
210 lines
8.2 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.
|
|
*
|
|
* 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);
|
|
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;
|
|
}
|