]> git.draconx.ca Git - gob-dx.git/blobdiff - src/main.c
Implement chaining of interface methods.
[gob-dx.git] / src / main.c
index 424f12c0fcfd48584e88a4e571d1fe0fb9329ee6..c7171c50fc441390d82d9d9cdf7b43926a650384 100644 (file)
@@ -2,6 +2,7 @@
  * Copyright (C) 1999,2000 the Free Software Foundation.
  * Copyright (C) 2000 Eazel, Inc.
  * Copyright (C) 2001-2011 George (Jiri) Lebl
+ * Copyright © 2019-2020 Nick Bowler
  *
  * Author: George (Jiri) Lebl
  *
@@ -953,6 +954,59 @@ add_signal_prots(Method *m)
        out_printf (out, "}\n\n");
 }
 
+static char *
+interface_type(const char *if_name)
+{
+       char *rawtype = remove_sep(if_name);
+       char *end = "", *typename;
+
+       if (!gtk3_ok) {
+               /*
+                * EEEK! evil, we should have some sort of option
+                * to force this for arbitrary interfaces, since
+                * some are Class and some are Iface.  Glib is shite
+                * in consistency.
+                */
+
+               if (strcmp (rawtype, "GtkEditable") == 0
+                   || strcmp (rawtype, "GTypePlugin") == 0)
+               {
+                       end = "Class";
+               } else {
+                       /* We'll assume Iface is the standard ending */
+                       end = "Iface";
+               }
+       } else {
+               /* GTK3 doesn't need Iface end */
+               end = "Interface";
+       }
+
+       typename = g_strconcat(rawtype, end, (char *)NULL);
+       g_free(rawtype);
+
+       return typename;
+}
+
+static void
+define_parent_interface_refs(Class *c)
+{
+       GList *li;
+
+       if (!c->interfaces)
+               return;
+
+       out_printf(out, "\n/* parent class interface implementations */\n");
+       for (li = c->interfaces; li != NULL; li = li->next) {
+               char *name = replace_sep(li->data, '_');
+               char *type = interface_type(li->data);
+
+               out_printf (out, "static %s *%s_parent_iface;\n", type, name);
+
+               g_free(name);
+               g_free(type);
+       }
+}
+
 static void
 add_enums(Class *c)
 {
@@ -1002,7 +1056,9 @@ add_enums(Class *c)
                           "static guint object_signals[LAST_SIGNAL] = {0};\n\n");
 
        out_printf(out, "/* pointer to the class of our parent */\n");
-       out_printf(out, "static %sClass *parent_class = NULL;\n\n", ptypebase);
+       out_printf(out, "static %sClass *parent_class = NULL;\n", ptypebase);
+       define_parent_interface_refs(c);
+       out_printf(out, "\n");
 }
 
 static void
@@ -1036,7 +1092,7 @@ add_interface_methods (Class *c, const char *interface)
 }
 
 static void
-add_interface_inits (Class *c)
+add_interface_inits(Class *c)
 {
        GList *li;
 
@@ -1046,39 +1102,19 @@ add_interface_inits (Class *c)
        out_printf(out, "\n");
 
        for (li = c->interfaces; li != NULL; li = li->next) {
-               const char *interface = li->data;
-               const char *end;
-               char *name = replace_sep (interface, '_');
-               char *type = remove_sep (interface);
+               char *name = replace_sep(li->data, '_');
+               char *type = interface_type(li->data);
 
-               if(!gtk3_ok)
-               {
-                       /* EEEK! evil, we should have some sort of option
-                        * to force this for arbitrary interfaces, since
-                        * some are Class and some are Iface.  Glib is shite
-                        * in consistency. */
-               
-                       if (strcmp (type, "GtkEditable") == 0 ||
-                           strcmp (type, "GTypePlugin") == 0)
-                               end = "Class";
-                       else
-                               // We'll assume Iface is the standard ending 
-                               end = "Iface";
-                       }
-               else
-               {
-                       /*GTK3 doesn't need Iface end*/
-                       end="Interface";
-               }
-               
-               out_printf (out, "\nstatic void\n"
-                           "___%s_init (%s%s *iface)\n"
-                           "{\n",
-                           name, type, end);
+               out_printf(out, "static void\n"
+                               "___%s_init (%s *iface)\n"
+                               "{\n", name, type);
 
-               add_interface_methods (c, interface);
+               add_interface_methods(c, li->data);
 
-               out_printf (out, "}\n\n");
+               out_printf(out, "\t%s_parent_iface\n", name);
+               out_printf(out, for_cpp ? "\t\t= (%s *)" : "\t\t= ", type);
+               out_printf(out, "g_type_interface_peek_parent(iface);\n"
+                               "}\n\n");
 
                g_free (name);
                g_free (type);
@@ -1129,20 +1165,46 @@ add_interfaces (void)
 static void
 add_dynamic_interfaces(void)
 {
-       GList *li;
-       for (li = ((Class *)class)->interfaces;
-            li != NULL;
-            li = li ->next) {
-               char *name = replace_sep (li->data, '_');
-               char *type = make_pre_macro (li->data, "TYPE");
+       GList *li = ((Class *)class)->interfaces;
 
-               out_printf(out,
-                          "\t\tg_type_module_add_interface(\n"
-                          "\t\t\ttype_module,\n"
-                          "\t\t\t%s_type_id,\n"
-                          "\t\t\t%s,\n"
-                          "\t\t\t&%s_info);\n",
-                          funcbase, type, name);
+       if (li) {
+               /*
+                * Hack to work around bug in g_type_module_add_interface,
+                * which will fail to add an interface to types that derive
+                * from something that also implements the same interface.
+                *
+                * The actual GType system does not have any such problem,
+                * and the GTypeModule implementation details relied upon
+                * here have not changed once since the feature was first
+                * implemented almost 20 years ago.
+                */
+               out_printf(out, "\t\tstruct _ModuleInterfaceInfo {\n"
+                               "\t\t\tgboolean loaded;\n"
+                               "\t\t\tGType instance_type;\n"
+                               "\t\t\tGType interface_type;\n"
+                               "\t\t\tGInterfaceInfo info;\n"
+                               "\t\t} *modinfo;\n");
+       }
+
+       for (; li; li = li->next) {
+               char *name = replace_sep(li->data, '_');
+               char *type = make_pre_macro(li->data, "TYPE");
+
+               out_printf(out, "\n"
+                               "\t\tmodinfo = g_malloc(sizeof *modinfo);\n"
+                               "\t\tmodinfo->loaded = TRUE;\n"
+                               "\t\tmodinfo->instance_type = %s_type_id;\n"
+                               "\t\tmodinfo->interface_type = %s;\n"
+                               "\t\tmodinfo->info = %s_info;\n"
+                               "\t\tg_type_add_interface_dynamic\n"
+                               "\t\t\t( modinfo->instance_type\n"
+                               "\t\t\t, modinfo->interface_type\n"
+                               "\t\t\t, G_TYPE_PLUGIN(type_module)\n"
+                               "\t\t\t);\n"
+                               "\t\ttype_module->interface_infos = g_slist_prepend\n"
+                               "\t\t\t( type_module->interface_infos\n"
+                               "\t\t\t, modinfo\n"
+                               "\t\t\t);\n", funcbase, type, name);
 
                g_free(type);
                g_free(name);
@@ -3026,6 +3088,35 @@ get_arg_names_for_macro (Method *m)
        return g_string_free (gs, FALSE);
 }
 
+static gboolean method_is_void(Method *m)
+{
+       return !strcmp(m->mtype->name, "void") && !m->mtype->pointer;
+}
+
+static const char *method_err_retval(Method *m)
+{
+       if (method_is_void(m))
+               return "(void)0";
+       if (m->onerror)
+               return m->onerror;
+       return "0";
+}
+
+static void
+put_interface_parent_handler(Method *m)
+{
+       const char *errval = method_err_retval(m);
+       char *name = replace_sep(m->interface, '_');
+       char *args = get_arg_names_for_macro(m);
+
+       out_printf(out, "#define PARENT_HANDLER(%s) (%s_parent_iface \\\n"
+                       "\t? %s_parent_iface->%s(%s) \\\n"
+                       "\t: %s)\n", args, name, name, m->id, args, errval);
+
+       g_free(name);
+       g_free(args);
+}
+
 static void
 put_method(Method *m)
 {
@@ -3041,6 +3132,7 @@ put_method(Method *m)
                        g_free(doc);
                }
        }
+
        switch(m->method) {
        case REGULAR_METHOD:
                if(m->line_no > 0)
@@ -3051,7 +3143,18 @@ put_method(Method *m)
                else /* PUBLIC, PROTECTED */
                        print_method(out, "", "\n", "", " ", "", "\n",
                                     m, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE);
+
+               if (m->interface) {
+                       out_addline_outfile(out);
+                       put_interface_parent_handler(m);
+               }
+
                print_method_body(m, TRUE, TRUE);
+
+               if (m->interface) {
+                       out_printf(out, "#undef PARENT_HANDLER\n");
+               }
+
                /* the outfile line was added above */
                break;
        case SIGNAL_FIRST_METHOD: