Ryujinx-GtkSharp/generator/Signal.cs
Mike Kestner 39ac15b6b6 2008-05-02 Mike Kestner <mkestner@novell.com>
* 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
2008-05-02 17:10:05 +00:00

473 lines
18 KiB
C#

// GtkSharp.Generation.Signal.cs - The Signal Generatable.
//
// Author: Mike Kestner <mkestner@speakeasy.net>
//
// Copyright (c) 2001-2003 Mike Kestner
// Copyright (c) 2003-2005 Novell, Inc.
// Copyright (c) 2007 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
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
namespace GtkSharp.Generation {
using System;
using System.Collections;
using System.IO;
using System.Xml;
public class Signal {
private string name;
private XmlElement elem;
private ReturnValue retval;
private Parameters parms;
private ClassBase container_type;
public Signal (XmlElement elem, ClassBase container_type)
{
this.elem = elem;
name = elem.GetAttribute ("name");
retval = new ReturnValue (elem ["return-type"]);
parms = new Parameters (elem["parameters"]);
this.container_type = container_type;
}
public string Name {
get {
return name;
}
set {
name = value;
}
}
public bool Validate ()
{
if (Name == "") {
Console.Write ("Nameless signal ");
Statistics.ThrottledCount++;
return false;
}
if (!parms.Validate () || !retval.Validate ()) {
Console.Write (" in signal " + Name + " ");
Statistics.ThrottledCount++;
return false;
}
return true;
}
public void GenerateDecl (StreamWriter sw)
{
if (elem.HasAttribute("new_flag") || (container_type != null && container_type.GetSignalRecursively (Name) != null))
sw.Write("new ");
sw.WriteLine ("\t\tevent " + EventHandlerQualifiedName + " " + Name + ";");
}
public string CName {
get {
return "\"" + elem.GetAttribute("cname") + "\"";
}
}
private string EventArgsName {
get {
if (IsEventHandler)
return "EventArgs";
else
return Name + "Args";
}
}
private string EventArgsQualifiedName {
get {
if (IsEventHandler)
return "System.EventArgs";
else
return container_type.NS + "." + Name + "Args";
}
}
private string EventHandlerName {
get {
if (IsEventHandler)
return "EventHandler";
else if (SymbolTable.Table [container_type.NS + Name + "Handler"] != null)
return Name + "EventHandler";
else
return Name + "Handler";
}
}
private string EventHandlerQualifiedName {
get {
if (IsEventHandler)
return "System.EventHandler";
else
return container_type.NS + "." + EventHandlerName;
}
}
string ClassFieldName {
get {
return elem.HasAttribute ("field_name") ? elem.GetAttribute("field_name") : String.Empty;
}
}
private bool HasOutParams {
get {
foreach (Parameter p in parms) {
if (p.PassAs == "out")
return true;
}
return false;
}
}
private bool IsEventHandler {
get {
return retval.CSType == "void" && parms.Count == 1 && (parms [0].Generatable is ObjectGen || parms [0].Generatable is InterfaceGen);
}
}
private bool IsVoid {
get {
return retval.CSType == "void";
}
}
private string ReturnGType {
get {
IGeneratable igen = SymbolTable.Table [retval.CType];
if (igen is ObjectGen)
return "GLib.GType.Object";
if (igen is BoxedGen)
return retval.CSType + ".GType";
if (igen is EnumGen)
return retval.CSType + "GType.GType";
switch (retval.CSType) {
case "bool":
return "GLib.GType.Boolean";
case "string":
return "GLib.GType.String";
case "int":
return "GLib.GType.Int";
default:
throw new Exception (retval.CSType);
}
}
}
private bool NeedNew (ClassBase implementor)
{
return elem.HasAttribute ("new_flag") ||
(container_type != null && container_type.GetSignalRecursively (Name) != null) ||
(implementor != null && implementor.GetSignalRecursively (Name) != null);
}
public void GenEventHandler (GenerationInfo gen_info)
{
if (IsEventHandler)
return;
string ns = container_type.NS;
StreamWriter sw = gen_info.OpenStream (EventHandlerName);
sw.WriteLine ("namespace " + ns + " {");
sw.WriteLine ();
sw.WriteLine ("\tusing System;");
sw.WriteLine ();
sw.WriteLine ("\tpublic delegate void " + EventHandlerName + "(object o, " + EventArgsName + " args);");
sw.WriteLine ();
sw.WriteLine ("\tpublic class " + EventArgsName + " : GLib.SignalArgs {");
for (int i = 1; i < parms.Count; i++) {
sw.WriteLine ("\t\tpublic " + parms[i].CSType + " " + parms[i].StudlyName + "{");
if (parms[i].PassAs != "out") {
sw.WriteLine ("\t\t\tget {");
sw.WriteLine ("\t\t\t\treturn (" + parms[i].CSType + ") Args[" + (i - 1) + "];");
sw.WriteLine ("\t\t\t}");
}
if (parms[i].PassAs != "") {
sw.WriteLine ("\t\t\tset {");
sw.WriteLine ("\t\t\t\tArgs[" + (i - 1) + "] = (" + parms[i].CSType + ")value;");
sw.WriteLine ("\t\t\t}");
}
sw.WriteLine ("\t\t}");
sw.WriteLine ();
}
sw.WriteLine ("\t}");
sw.WriteLine ("}");
sw.Close ();
}
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 +"\")]");
sw.Write ("\t\tprotected ");
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;");
else
sw.WriteLine ("\t\t\tGLib.Value ret = new GLib.Value (" + ReturnGType + ");");
sw.WriteLine ("\t\t\tGLib.ValueArray inst_and_params = new GLib.ValueArray (" + parms.Count + ");");
sw.WriteLine ("\t\t\tGLib.Value[] vals = new GLib.Value [" + parms.Count + "];");
sw.WriteLine ("\t\t\tvals [0] = new GLib.Value (this);");
sw.WriteLine ("\t\t\tinst_and_params.Append (vals [0]);");
string cleanup = "";
for (int i = 1; i < parms.Count; i++) {
Parameter p = parms [i];
if (p.PassAs != "") {
if (SymbolTable.Table.IsBoxed (p.CType)) {
if (p.PassAs == "ref")
sw.WriteLine ("\t\t\tvals [" + i + "] = new GLib.Value (" + p.Name + ");");
else
sw.WriteLine ("\t\t\tvals [" + i + "] = new GLib.Value ((GLib.GType)typeof (" + p.CSType + "));");
cleanup += "\t\t\t" + p.Name + " = (" + p.CSType + ") vals [" + i + "];\n";
} else {
if (p.PassAs == "ref")
sw.WriteLine ("\t\t\tIntPtr " + p.Name + "_ptr = GLib.Marshaller.StructureToPtrAlloc (" + p.Generatable.CallByName (p.Name) + ");");
else
sw.WriteLine ("\t\t\tIntPtr " + p.Name + "_ptr = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (" + p.MarshalType + ")));");
sw.WriteLine ("\t\t\tvals [" + i + "] = new GLib.Value (" + p.Name + "_ptr);");
cleanup += "\t\t\t" + p.Name + " = " + p.FromNative ("(" + p.MarshalType + ") Marshal.PtrToStructure (" + p.Name + "_ptr, typeof (" + p.MarshalType + "))") + ";\n";
cleanup += "\t\t\tMarshal.FreeHGlobal (" + p.Name + "_ptr);\n";
}
} else if (p.IsLength && parms [i - 1].IsString)
sw.WriteLine ("\t\t\tvals [" + i + "] = new GLib.Value (System.Text.Encoding.UTF8.GetByteCount (" + parms [i-1].Name + "));");
else
sw.WriteLine ("\t\t\tvals [" + i + "] = new GLib.Value (" + p.Name + ");");
sw.WriteLine ("\t\t\tinst_and_params.Append (vals [" + i + "]);");
}
sw.WriteLine ("\t\t\tg_signal_chain_from_overridden (inst_and_params.ArrayPtr, ref ret);");
if (cleanup != "")
sw.WriteLine (cleanup);
sw.WriteLine ("\t\t\tforeach (GLib.Value v in vals)");
sw.WriteLine ("\t\t\t\tv.Dispose ();");
if (!IsVoid) {
IGeneratable igen = SymbolTable.Table [retval.CType];
sw.WriteLine ("\t\t\t" + retval.CSType + " result = (" + (igen is EnumGen ? retval.CSType + ") (Enum" : retval.CSType) + ") ret;");
sw.WriteLine ("\t\t\tret.Dispose ();");
sw.WriteLine ("\t\t\treturn result;");
}
sw.WriteLine ("\t\t}\n");
}
private void GenDefaultHandlerDelegate (GenerationInfo gen_info, ClassBase implementor)
{
StreamWriter sw = gen_info.Writer;
StreamWriter glue;
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]");
sw.WriteLine ("\t\tdelegate " + retval.ToNativeType + " " + Name + "VMDelegate (" + parms.ImportSignature + ");\n");
if (use_glue) {
glue = gen_info.GlueWriter;
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);
glue.WriteLine ("void\n{0} (GType gtype, gpointer cb)", glue_name);
glue.WriteLine ("{");
glue.WriteLine ("\tGObjectClass *klass = g_type_class_peek (gtype);");
glue.WriteLine ("\tif (klass == NULL)");
glue.WriteLine ("\t\tklass = g_type_class_ref (gtype);");
glue.WriteLine ("\t(({0} *)klass)->{1} = cb;", container_type.CName + "Class", ClassFieldName);
glue.WriteLine ("}\n");
}
sw.WriteLine ("\t\tstatic {0} {1};\n", Name + "VMDelegate", Name + "VMCallback");
sw.WriteLine ("\t\tstatic " + retval.ToNativeType + " " + Name.ToLower() + "_cb (" + parms.ImportSignature + ")");
sw.WriteLine ("\t\t{");
sw.WriteLine ("\t\t\ttry {");
sw.WriteLine ("\t\t\t\t{0} {1}_managed = GLib.Object.GetObject ({1}, false) as {0};", implementor != null ? implementor.Name : container_type.Name, parms[0].Name);
sw.Write (call.Setup ("\t\t\t\t"));
sw.Write ("\t\t\t\t{0}", IsVoid ? "" : retval.CSType == retval.ToNativeType ? "return " : retval.CSType + " raw_ret = ");
sw.WriteLine ("{2}_managed.{0} ({1});", "On" + Name, call.ToString (), parms[0].Name);
sw.Write (call.Finish ("\t\t\t\t"));
if (!IsVoid && retval.CSType != retval.ToNativeType)
sw.WriteLine ("\t\t\t\treturn {0};", SymbolTable.Table.ToNativeReturn (retval.CType, "raw_ret"));
sw.WriteLine ("\t\t\t} catch (Exception e) {");
bool fatal = HasOutParams || !IsVoid;
sw.WriteLine ("\t\t\t\tGLib.ExceptionManager.RaiseUnhandledException (e, " + (fatal ? "true" : "false") + ");");
if (fatal) {
sw.WriteLine ("\t\t\t\t// NOTREACHED: above call doesn't return");
sw.WriteLine ("\t\t\t\tthrow e;");
}
sw.WriteLine ("\t\t\t}");
sw.WriteLine ("\t\t}\n");
sw.WriteLine ("\t\tprivate static void Override" + Name + " (GLib.GType gtype)");
sw.WriteLine ("\t\t{");
sw.WriteLine ("\t\t\tif (" + Name + "VMCallback == null)");
sw.WriteLine ("\t\t\t\t" + Name + "VMCallback = new " + Name + "VMDelegate (" + Name.ToLower() + "_cb);");
if (use_glue)
sw.WriteLine ("\t\t\t{0} (gtype.Val, {1}VMCallback);", glue_name, Name);
else
sw.WriteLine ("\t\t\tOverrideVirtualMethod (gtype, " + CName + ", " + Name + "VMCallback);");
sw.WriteLine ("\t\t}\n");
}
public void GenEvent (StreamWriter sw, ClassBase implementor, string target)
{
string args_type = IsEventHandler ? "" : ", typeof (" + EventArgsQualifiedName + ")";
sw.WriteLine("\t\t[GLib.Signal("+ CName + ")]");
sw.Write("\t\tpublic ");
if (NeedNew (implementor))
sw.Write("new ");
sw.WriteLine("event " + EventHandlerQualifiedName + " " + Name + " {");
sw.WriteLine("\t\t\tadd {");
sw.WriteLine("\t\t\t\tGLib.Signal sig = GLib.Signal.Lookup (" + target + ", " + CName + args_type + ");");
sw.WriteLine("\t\t\t\tsig.AddDelegate (value);");
sw.WriteLine("\t\t\t}");
sw.WriteLine("\t\t\tremove {");
sw.WriteLine("\t\t\t\tGLib.Signal sig = GLib.Signal.Lookup (" + target + ", " + CName + args_type + ");");
sw.WriteLine("\t\t\t\tsig.RemoveDelegate (value);");
sw.WriteLine("\t\t\t}");
sw.WriteLine("\t\t}");
sw.WriteLine();
}
public void Generate (GenerationInfo gen_info, ClassBase implementor)
{
StreamWriter sw = gen_info.Writer;
if (implementor == null)
GenEventHandler (gen_info);
GenDefaultHandlerDelegate (gen_info, implementor);
if (gen_info.GlueEnabled && implementor == null && ClassFieldName.Length > 0)
GenGlueVirtualMethod (gen_info);
else
GenChainVirtualMethod (sw, implementor);
GenEvent (sw, implementor, "this");
Statistics.SignalCount++;
}
}
}