From 39ac15b6b65a6b4b6b22c2453cba171af46b9a37 Mon Sep 17 00:00:00 2001 From: Mike Kestner Date: Fri, 2 May 2008 17:10:05 +0000 Subject: [PATCH] 2008-05-02 Mike Kestner * generator/GenerationInfo.cs: refactor glue writer implementation so that GlueEnabled means there is a valid glue writer available. Avoids crashes in scenarios where an unwriteable glue path is provided to the generator. Generate a glue function which scans the type hierarchy of an object for the most-derived unmanaged ancestor so that we can invoke class methods on it, avoiding infinite recursions. * generator/Signal.cs: revamp the default handler vm overriding mechanism. When class fields exist which can be directly hooked into, we now generate glue to override and chain up to unmanaged base funcs. This avoids some strangeness in the g_signal_override_class_closure and g_signal_chain_from_overridden reported in #332300 and also lays the groundwork for automated generation of non-signal VMs. * gtk/Gtk.metadata: block signal glue generation for a few types which don't seem to install headers. svn path=/trunk/gtk-sharp/; revision=102350 --- ChangeLog | 17 ++++++ generator/GenerationInfo.cs | 81 +++++++++++++++------------ generator/Signal.cs | 107 ++++++++++++++++++++++++++++++++++-- gtk/Gtk.metadata | 4 ++ 4 files changed, 168 insertions(+), 41 deletions(-) diff --git a/ChangeLog b/ChangeLog index 31efecac8..32133d900 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2008-05-02 Mike Kestner + + * generator/GenerationInfo.cs: refactor glue writer implementation + so that GlueEnabled means there is a valid glue writer available. + Avoids crashes in scenarios where an unwriteable glue path is provided + to the generator. Generate a glue function which scans the type + hierarchy of an object for the most-derived unmanaged ancestor so + that we can invoke class methods on it, avoiding infinite recursions. + * generator/Signal.cs: revamp the default handler vm overriding + mechanism. When class fields exist which can be directly hooked into, + we now generate glue to override and chain up to unmanaged base funcs. + This avoids some strangeness in the g_signal_override_class_closure + and g_signal_chain_from_overridden reported in #332300 and also lays + the groundwork for automated generation of non-signal VMs. + * gtk/Gtk.metadata: block signal glue generation for a few types which + don't seem to install headers. + 2008-05-02 Mike Kestner * glib/Object.cs: Don't bother hooking VM into the class field diff --git a/generator/GenerationInfo.cs b/generator/GenerationInfo.cs index ab9f6ecaf..7b7619a0a 100644 --- a/generator/GenerationInfo.cs +++ b/generator/GenerationInfo.cs @@ -2,7 +2,7 @@ // // Author: Mike Kestner // -// Copyright (c) 2003-2005 Novell Inc. +// Copyright (c) 2003-2008 Novell Inc. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public @@ -31,10 +31,10 @@ namespace GtkSharp.Generation { string dir; string custom_dir; string assembly_name; - string glue_filename; - string glue_includes; string gluelib_name; + bool glue_enabled; StreamWriter sw; + StreamWriter glue_sw; public GenerationInfo (XmlElement ns) { @@ -43,9 +43,6 @@ namespace GtkSharp.Generation { dir = ".." + sep + ns_name.ToLower () + sep + "generated"; custom_dir = ".." + sep + ns_name.ToLower (); assembly_name = ns_name.ToLower () + "-sharp"; - gluelib_name = ""; - glue_filename = ""; - glue_includes = ""; } public GenerationInfo (string dir, string assembly_name) : this (dir, dir, assembly_name, "", "", "") {} @@ -55,9 +52,48 @@ namespace GtkSharp.Generation { this.dir = dir; this.custom_dir = custom_dir; this.assembly_name = assembly_name; - this.glue_filename = glue_filename; - this.glue_includes = glue_includes; this.gluelib_name = gluelib_name; + InitializeGlue (glue_filename, glue_includes, gluelib_name); + } + + void InitializeGlue (string glue_filename, string glue_includes, string gluelib_name) + { + if (gluelib_name != String.Empty && glue_filename != String.Empty) { + FileStream stream; + try { + stream = new FileStream (glue_filename, FileMode.Create, FileAccess.Write); + } catch (Exception) { + Console.Error.WriteLine ("Unable to create specified glue file. Glue will not be generated."); + return; + } + + glue_sw = new StreamWriter (stream); + + glue_sw.WriteLine ("// This file was generated by the Gtk# code generator."); + glue_sw.WriteLine ("// Any changes made will be lost if regenerated."); + glue_sw.WriteLine (); + + if (glue_includes != "") { + foreach (string header in glue_includes.Split (new char[] {',', ' '})) { + if (header != "") + glue_sw.WriteLine ("#include <{0}>", header); + } + glue_sw.WriteLine (""); + } + glue_sw.WriteLine ("const gchar *__prefix = \"__gtksharp_\";\n"); + glue_sw.WriteLine ("#define HAS_PREFIX(a) (*((guint64 *)(a)) == *((guint64 *) __prefix))\n"); + glue_sw.WriteLine ("static GObjectClass *"); + glue_sw.WriteLine ("get_threshold_class (GObject *obj)"); + glue_sw.WriteLine ("{"); + glue_sw.WriteLine ("\tGType gtype = G_TYPE_FROM_INSTANCE (obj);"); + glue_sw.WriteLine ("\twhile (HAS_PREFIX (g_type_name (gtype)))"); + glue_sw.WriteLine ("\t\tgtype = g_type_parent (gtype);"); + glue_sw.WriteLine ("\tGObjectClass *klass = g_type_class_peek (gtype);"); + glue_sw.WriteLine ("\tif (klass == NULL) klass = g_type_class_ref (gtype);"); + glue_sw.WriteLine ("\treturn klass;"); + glue_sw.WriteLine ("}\n"); + glue_enabled = true; + } } public string AssemblyName { @@ -86,39 +122,12 @@ namespace GtkSharp.Generation { public bool GlueEnabled { get { - return gluelib_name != String.Empty && glue_filename != String.Empty; + return glue_enabled; } } - public string GlueFilename { - get { - return glue_filename; - } - } - - StreamWriter glue_sw = null; public StreamWriter GlueWriter { get { - if (!GlueEnabled) - return null; - - if (glue_sw == null) { - FileStream stream = new FileStream (glue_filename, FileMode.Create, FileAccess.Write); - glue_sw = new StreamWriter (stream); - - glue_sw.WriteLine ("// This file was generated by the Gtk# code generator."); - glue_sw.WriteLine ("// Any changes made will be lost if regenerated."); - glue_sw.WriteLine (); - - if (glue_includes != "") { - foreach (string header in glue_includes.Split (new char[] {',', ' '})) { - if (header != "") - glue_sw.WriteLine ("#include <{0}>", header); - } - glue_sw.WriteLine (""); - } - } - return glue_sw; } } diff --git a/generator/Signal.cs b/generator/Signal.cs index 1491e817f..22ceead1d 100644 --- a/generator/Signal.cs +++ b/generator/Signal.cs @@ -219,7 +219,7 @@ namespace GtkSharp.Generation { sw.Close (); } - private void GenVirtualMethod (StreamWriter sw, ClassBase implementor) + private void GenVMDeclaration (StreamWriter sw, ClassBase implementor) { VMSignature vmsig = new VMSignature (parms); sw.WriteLine ("\t\t[GLib.DefaultSignalHandler(Type=typeof(" + (implementor != null ? implementor.QualifiedName : container_type.QualifiedName) + "), ConnectionMethod=\"Override" + Name +"\")]"); @@ -227,6 +227,101 @@ namespace GtkSharp.Generation { if (NeedNew (implementor)) sw.Write ("new "); sw.WriteLine ("virtual {0} {1} ({2})", retval.CSType, "On" + Name, vmsig.ToString ()); + } + + private string CastFromInt (string type) + { + return type != "int" ? "(" + type + ") " : ""; + } + + private string GlueCallString { + get { + string result = "Handle"; + + for (int i = 1; i < parms.Count; i++) { + Parameter p = parms [i]; + IGeneratable igen = p.Generatable; + + if (i > 1 && parms [i - 1].IsString && p.IsLength && p.PassAs == String.Empty) { + string string_name = parms [i - 1].Name; + result += ", " + igen.CallByName (CastFromInt (p.CSType) + "System.Text.Encoding.UTF8.GetByteCount (" + string_name + ")"); + continue; + } + + p.CallName = p.Name; + string call_parm = p.CallString; + + if (p.IsUserData && parms.IsHidden (p) && !parms.HideData && (i == 1 || parms [i - 1].Scope != "notified")) { + call_parm = "IntPtr.Zero"; + } + + result += ", " + call_parm; + } + return result; + } + } + + private string GlueSignature { + get { + string result = String.Empty; + for (int i = 0; i < parms.Count; i++) + result += parms[i].CType.Replace ("const-", "const ") + " " + parms[i].Name + (i == parms.Count-1 ? "" : ", "); + return result; + } + } + + private string DefaultGlueValue { + get { + string val = retval.DefaultValue; + switch (val) { + case "null": + return "NULL"; + case "false": + return "FALSE"; + case "true": + return "TRUE"; + default: + return val; + } + } + } + + private void GenGlueVirtualMethod (GenerationInfo gen_info) + { + StreamWriter glue = gen_info.GlueWriter; + string glue_name = String.Format ("{0}sharp_{1}_base_{2}", container_type.NS.ToLower ().Replace (".", "_"), container_type.Name.ToLower (), ClassFieldName); + glue.WriteLine ("{0} {1} ({2});\n", retval.CType, glue_name, GlueSignature); + glue.WriteLine ("{0}\n{1} ({2})", retval.CType, glue_name, GlueSignature); + glue.WriteLine ("{"); + glue.WriteLine ("\t{0}Class *klass = ({0}Class *) get_threshold_class (G_OBJECT ({1}));", container_type.CName, parms[0].Name); + glue.Write ("\tif (klass->{0})\n\t\t", ClassFieldName); + if (!IsVoid) + glue.Write ("return "); + glue.Write ("(* klass->{0}) (", ClassFieldName); + for (int i = 0; i < parms.Count; i++) + glue.Write (parms[i].Name + (i == parms.Count - 1 ? "" : ", ")); + glue.WriteLine (");"); + if (!IsVoid) + glue.WriteLine ("\treturn " + DefaultGlueValue + ";"); + glue.WriteLine ("}"); + + StreamWriter sw = gen_info.Writer; + sw.WriteLine ("\t\t[DllImport (\"{0}\")]", gen_info.GluelibName); + sw.WriteLine ("\t\tstatic extern {0} {1} ({2});\n", retval.MarshalType, glue_name, parms.ImportSignature); + GenVMDeclaration (sw, null); + sw.WriteLine ("\t\t{"); + MethodBody body = new MethodBody (parms); + body.Initialize (gen_info, false, false, String.Empty); + sw.WriteLine ("\t\t\t{0}{1} ({2});", IsVoid ? "" : retval.MarshalType + " __ret = ", glue_name, GlueCallString); + body.Finish (sw, ""); + if (!IsVoid) + sw.WriteLine ("\t\t\treturn {0};", retval.FromNative ("__ret")); + sw.WriteLine ("\t\t}\n"); + } + + private void GenChainVirtualMethod (StreamWriter sw, ClassBase implementor) + { + GenVMDeclaration (sw, implementor); sw.WriteLine ("\t\t{"); if (IsVoid) sw.WriteLine ("\t\t\tGLib.Value ret = GLib.Value.Empty;"); @@ -283,8 +378,7 @@ namespace GtkSharp.Generation { { StreamWriter sw = gen_info.Writer; StreamWriter glue; - bool use_glue = false; - //bool use_glue = glue != null && implementor == null && ClassFieldName.Length > 0; + bool use_glue = gen_info.GlueEnabled && implementor == null && ClassFieldName.Length > 0; string glue_name = String.Empty; ManagedCallString call = new ManagedCallString (parms); sw.WriteLine ("\t\t[GLib.CDeclCallback]"); @@ -292,7 +386,7 @@ namespace GtkSharp.Generation { if (use_glue) { glue = gen_info.GlueWriter; - glue_name = String.Format ("{0}sharp_{1}_{2}", container_type.NS.ToLower ().Replace (".", "_"), container_type.Name.ToLower (), ClassFieldName); + glue_name = String.Format ("{0}sharp_{1}_override_{2}", container_type.NS.ToLower ().Replace (".", "_"), container_type.Name.ToLower (), ClassFieldName); sw.WriteLine ("\t\t[DllImport (\"{0}\")]", gen_info.GluelibName); sw.WriteLine ("\t\tstatic extern void {0} (IntPtr gtype, {1}VMDelegate cb);\n", glue_name, Name); glue.WriteLine ("void {0} (GType gtype, gpointer cb);\n", glue_name); @@ -365,7 +459,10 @@ namespace GtkSharp.Generation { GenEventHandler (gen_info); GenDefaultHandlerDelegate (gen_info, implementor); - GenVirtualMethod (sw, implementor); + if (gen_info.GlueEnabled && implementor == null && ClassFieldName.Length > 0) + GenGlueVirtualMethod (gen_info); + else + GenChainVirtualMethod (sw, implementor); GenEvent (sw, implementor, "this"); Statistics.SignalCount++; diff --git a/gtk/Gtk.metadata b/gtk/Gtk.metadata index 9f8a08fff..7cebf88c2 100644 --- a/gtk/Gtk.metadata +++ b/gtk/Gtk.metadata @@ -281,6 +281,7 @@ true 1 1 + out 1 out @@ -379,6 +380,7 @@ 1 1 1 + 1 1 1 @@ -502,8 +504,10 @@ GtkPageSetup* true true + call call + const-gfilename* call const-gfilename*