// GtkSharp.Generation.GObjectVM.cs - GObject specific part of VM creation // // Author: Christian Hoff // // Copyright (c) 2007 Novell, Inc. // Copyright (c) 2009 Christian Hoff // // 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 GObjectVM : VirtualMethod { protected string class_struct_name; const bool force_glue_generation = false; public GObjectVM (XmlElement elem, ObjectBase container_type) : base (elem, container_type) { parms.HideData = false; this.Protection = "protected"; class_struct_name = container_type.ClassStructName; } // Some types don't install headers. In that case, the glue code will not compile. bool BlockGlue { get { return elem.GetAttribute ("block_glue") == "1"; } } protected override string CallString { get { return String.Format ("{0} ({1})", IsStatic ? this.CName + "_handler" : "On" + this.Name, call.ToString ()); } } public void Generate (GenerationInfo gen_info, ObjectBase implementor) { gen_info.CurrentMember = Name; if (!CanGenerate (gen_info, implementor)) throw new NotSupportedException (String.Format ("Cannot generate virtual method {0}.{1}. Make sure a writable glue path was provided to the generator.", container_type.Name, this.CallString)); GenerateOverride (gen_info, implementor); GenerateCallback (gen_info.Writer, implementor); if (!IsStatic) GenerateUnmanagedInvocation (gen_info, implementor); } protected virtual bool CanGenerate (GenerationInfo gen_info, ObjectBase implementor) { if (implementor != null || this.CName.Length == 0 || CodeType == VMCodeType.None || (CodeType == VMCodeType.Glue && !gen_info.GlueEnabled)) return false; else return true; } enum VMCodeType { None, Managed, Glue } VMCodeType CodeType { get { if (!((ObjectBase)container_type).CanGenerateClassStruct || force_glue_generation) { if (BlockGlue) return VMCodeType.None; else return VMCodeType.Glue; } else return VMCodeType.Managed; } } enum VMOverrideType { Unspecified, DeclaringClass, ImplementingClass } /* There are basically two types of static virtual methods: * 1. VMs overridden in the declaring class (e.g. AtkUtil vms): * The VM is overridden in the class in which it is declared and not in the derived classes. In that case, the GAPI generates a static XYZHandler property * in the declaring class. * 2. VMs overridden in derived classes (e.g. GIO is_supported vms): * As with nonstatic vms, this VM type hooks into the class structure field of derived classes. This type is currently unsupported as it is rarely used * and we would need anonymous methods for the callback (we are using only *one* callback method; the callback does not know to which type that method call * has to be redirected). */ VMOverrideType OverrideType { get { if (IsStatic) { switch (elem.GetAttribute ("override_in")) { case "declaring_class": return VMOverrideType.DeclaringClass; case "implementing_class": return VMOverrideType.ImplementingClass; default: return VMOverrideType.Unspecified; } } else return VMOverrideType.ImplementingClass; } } protected virtual void GenerateOverride (GenerationInfo gen_info, ObjectBase implementor) { if (CodeType == VMCodeType.Glue) GenerateOverride_glue (gen_info); else GenerateOverride_managed (gen_info.Writer); } protected virtual void GenerateUnmanagedInvocation (GenerationInfo gen_info, ObjectBase implementor) { if (CodeType == VMCodeType.Glue) GenerateUnmanagedInvocation_glue (gen_info); else GenerateUnmanagedInvocation_managed (gen_info); } protected void GenerateOverrideBody (StreamWriter sw) { sw.WriteLine ("\t\tstatic {0}NativeDelegate {0}_cb_delegate;", Name); sw.WriteLine ("\t\tstatic " + Name + "NativeDelegate " + Name + "VMCallback {"); sw.WriteLine ("\t\t\tget {"); sw.WriteLine ("\t\t\t\tif ({0}_cb_delegate == null)", Name); sw.WriteLine ("\t\t\t\t\t{0}_cb_delegate = new {0}NativeDelegate ({0}_cb);", Name); sw.WriteLine ("\t\t\t\treturn {0}_cb_delegate;", Name); sw.WriteLine ("\t\t\t}"); sw.WriteLine ("\t\t}"); sw.WriteLine (); if (IsStatic) { sw.WriteLine ("\t\tpublic delegate {0} {1}Delegate ({2});", retval.CSType, Name, Signature.ToString ()); sw.WriteLine ("\t\tstatic {0}Delegate {1}_handler;", Name, CName); sw.WriteLine (); sw.WriteLine ("\t\tpublic static " + Name + "Delegate " + Name + "Handler {"); sw.WriteLine ("\t\t\tset {"); sw.WriteLine ("\t\t\t\t{0}_handler = value;", CName); sw.WriteLine ("\t\t\t\tOverride{0} ((GLib.GType) typeof ({1}), value == null ? null : {0}VMCallback);", Name, container_type.Name); sw.WriteLine ("\t\t\t}"); sw.WriteLine ("\t\t}"); } else { sw.WriteLine ("\t\tstatic void Override{0} (GLib.GType gtype)", this.Name); sw.WriteLine ("\t\t{"); sw.WriteLine ("\t\t\tOverride{0} (gtype, {0}VMCallback);", this.Name); sw.WriteLine ("\t\t}"); } sw.WriteLine (); sw.WriteLine ("\t\tstatic void Override{0} (GLib.GType gtype, {0}NativeDelegate callback)", this.Name); sw.WriteLine ("\t\t{"); } protected void GenerateOverride_managed (StreamWriter sw) { GenerateOverrideBody (sw); // Override VM; class_offset var is generated by object generatable sw.WriteLine("\t\t\tunsafe {"); sw.WriteLine("\t\t\t\tIntPtr* raw_ptr = (IntPtr*)(((long) gtype.GetClassPtr()) + (long) class_abi.GetFieldOffset(\"{0}\"));", CName); sw.WriteLine("\t\t\t\t*raw_ptr = Marshal.GetFunctionPointerForDelegate((Delegate) callback);"); sw.WriteLine("\t\t\t}"); sw.WriteLine("\t\t}"); sw.WriteLine (); } protected void GenerateMethodBody (StreamWriter sw, ClassBase implementor) { sw.WriteLine ("\t\t[GLib.DefaultSignalHandler(Type=typeof(" + (implementor != null ? implementor.QualifiedName : container_type.QualifiedName) + "), ConnectionMethod=\"Override" + this.Name +"\")]"); sw.Write ("\t\t{0} ", this.Protection); if (this.modifiers != "") sw.Write ("{0} ", this.modifiers); sw.WriteLine ("virtual {0} On{1} ({2})", retval.CSType, this.Name, Signature.ToString ()); sw.WriteLine ("\t\t{"); sw.WriteLine ("\t\t\t{0}Internal{1} ({2});", retval.IsVoid ? "" : "return ", this.Name, Signature.GetCallString (false)); sw.WriteLine ("\t\t}"); sw.WriteLine (); // This method is to be invoked from existing VM implementations in the custom code sw.WriteLine ("\t\tprivate {0} Internal{1} ({2})", retval.CSType, this.Name, Signature.ToString ()); sw.WriteLine ("\t\t{"); } void GenerateUnmanagedInvocation_managed (GenerationInfo gen_info) { StreamWriter sw = gen_info.Writer; string native_call = "this.Handle"; if (parms.Count > 0) native_call += ", " + Body.GetCallString (false); this.GenerateMethodBody (sw, null); // Find the first unmanaged ancestor sw.WriteLine ($"\t\t\t{Name}NativeDelegate unmanaged = class_abi.BaseOverride<{Name}NativeDelegate>(this.LookupGType(), \"{CName}\");"); sw.Write ("\t\t\tif (unmanaged == null) "); if (parms.HasOutParam) sw.WriteLine ("throw new InvalidOperationException (\"No base method to invoke\");"); else if (retval.IsVoid) sw.WriteLine ("return;"); else sw.WriteLine ("return {0};", retval.DefaultValue); sw.WriteLine (); Body.Initialize (gen_info); sw.Write ("\t\t\t"); if (!retval.IsVoid) sw.Write ("{0} __result = ", retval.MarshalType); sw.WriteLine ("unmanaged ({0});", native_call); Body.Finish (gen_info.Writer, ""); if(!retval.IsVoid) sw.WriteLine ("\t\t\treturn {0};", retval.FromNative ("__result")); sw.WriteLine ("\t\t}"); sw.WriteLine (); } /* old glue code. This code is to be used if * a) the generated api file is version 1 * b) an old Mono version(< 2.4) is being used * Punt it when we drop support for the parser version 1. */ private string CastFromInt (string type) { return type != "int" ? "(" + type + ") " : ""; } private string GlueSignature { get { string[] glue_params = new string [this.IsStatic ? parms.Count + 1 : parms.Count + 2]; glue_params [0] = class_struct_name + " *class_struct"; if (!IsStatic) glue_params [1] = container_type.CName + "* inst"; for (int i = 0; i < parms.Count; i++) glue_params [i + (IsStatic ? 1 : 2)] = parms [i].CType.Replace ("const-", "const ") + " " + parms [i].Name; return String.Join (", ", glue_params); } } private string DefaultGlueValue { get { if (retval.IGen is EnumGen) return String.Format ("({0}) 0", retval.CType); string val = retval.DefaultValue; switch (val) { case "null": return "NULL"; case "false": return "FALSE"; case "true": return "TRUE"; case "GLib.GType.None": return "G_TYPE_NONE"; default: return val; } } } void GenerateOverride_glue (GenerationInfo gen_info) { StreamWriter glue = gen_info.GlueWriter; StreamWriter sw = gen_info.Writer; string glue_name = String.Format ("{0}sharp_{1}_override_{2}", container_type.NS.ToLower ().Replace (".", "_"), container_type.Name.ToLower (), CName); sw.WriteLine ("\t\t[DllImport (\"{0}\")]", gen_info.GluelibName); sw.WriteLine ("\t\tstatic extern void {0} (IntPtr class_struct, {1}NativeDelegate cb);", glue_name, Name); sw.WriteLine (); glue.WriteLine ("void {0} ({1} *class_struct, gpointer cb);\n", glue_name, class_struct_name); glue.WriteLine ("void\n{0} ({1} *class_struct, gpointer cb)", glue_name, class_struct_name); glue.WriteLine ("{"); glue.WriteLine ("\tclass_struct->{0} = cb;", CName); glue.WriteLine ("}"); glue.WriteLine (); GenerateOverrideBody (sw); sw.WriteLine ("\t\t\t{0} (gtype.GetClassPtr (), callback);", glue_name); sw.WriteLine ("\t\t}"); sw.WriteLine (); } void GenerateUnmanagedInvocation_glue (GenerationInfo gen_info) { StreamWriter glue = gen_info.GlueWriter; string glue_name = String.Format ("{0}sharp_{1}_invoke_{2}", container_type.NS.ToLower ().Replace (".", "_"), container_type.Name.ToLower (), CName); glue.WriteLine ("{0} {1} ({2});\n", retval.CType.Replace ("const-", "const "), glue_name, GlueSignature); glue.WriteLine ("{0}\n{1} ({2})", retval.CType.Replace ("const-", "const "), glue_name, GlueSignature); glue.WriteLine ("{"); glue.Write ("\tif (class_struct->{0})\n\t\t", CName); if (!retval.IsVoid) glue.Write ("return "); string[] call_args = new string [IsStatic ? parms.Count : parms.Count + 1]; if (!IsStatic) call_args [0] = "inst"; for (int i = 0; i < parms.Count; i++) call_args [IsStatic ? i : i + 1] = parms[i].Name; glue.WriteLine ("(* class_struct->{0}) ({1});", CName, String.Join (", ", call_args)); if (!retval.IsVoid) glue.WriteLine ("\treturn " + DefaultGlueValue + ";"); glue.WriteLine ("}"); glue.WriteLine (); StreamWriter sw = gen_info.Writer; sw.WriteLine ("\t\t[DllImport (\"{0}\")]", gen_info.GluelibName); sw.Write ("\t\tstatic extern {0} {1} (IntPtr class_struct", retval.MarshalType, glue_name); if (!IsStatic) sw.Write (", IntPtr inst"); if (parms.Count > 0) sw.Write (", {0}", parms.ImportSignature); sw.WriteLine (");"); sw.WriteLine (); GenerateMethodBody (sw, null); Body.Initialize (gen_info, false, false, String.Empty); string glue_call_string = "this.LookupGType ().GetThresholdType ().GetClassPtr ()"; if (!IsStatic) glue_call_string += ", Handle"; if (parms.Count > 0) glue_call_string += ", " + Body.GetCallString (false); sw.Write ("\t\t\t"); if (!retval.IsVoid) sw.Write ("{0} __result = ", retval.MarshalType); sw.WriteLine ("{0} ({1});", glue_name, glue_call_string); Body.Finish (gen_info.Writer, ""); if(!retval.IsVoid) sw.WriteLine ("\t\t\treturn {0};", retval.FromNative ("__result")); sw.WriteLine ("\t\t}"); sw.WriteLine (); } public override bool Validate (LogWriter log) { if (!base.Validate (log)) return false; bool is_valid = true; if (this.IsStatic) { switch (OverrideType) { case VMOverrideType.Unspecified: log.Warn ("Static virtual methods can only be generated if you provide info on how to override this method via the metadata "); is_valid = false; break; case VMOverrideType.ImplementingClass: log.Warn ("Overriding static virtual methods in the implementing class is not supported yet "); is_valid = false; break; } } return is_valid; } } }