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*