From 29a900e51ef02c390c6f622c86e515b1b5ff4133 Mon Sep 17 00:00:00 2001 From: Stephan Sundermann Date: Wed, 9 Oct 2013 13:40:56 +0200 Subject: [PATCH] generator: added conversion for byref structs The pointer from native is stored inside of a class which wraps the structure. Fields can be accessed by marshalling from and to the pointer. glib: Value.Update does now invoke a private Update() method which is needed to update the new structures. --- generator/FieldBase.cs | 6 +- generator/Makefile.am | 1 + generator/NativeStructGen.cs | 232 +++++++++++++++++++++++++++++++++++ generator/ObjectField.cs | 2 +- generator/Parser.cs | 3 + generator/StructField.cs | 10 ++ generator/generator.csproj | 3 +- glib/Value.cs | 21 +++- 8 files changed, 268 insertions(+), 10 deletions(-) create mode 100644 generator/NativeStructGen.cs diff --git a/generator/FieldBase.cs b/generator/FieldBase.cs index 053e98aa9..8e927d601 100644 --- a/generator/FieldBase.cs +++ b/generator/FieldBase.cs @@ -40,7 +40,7 @@ namespace GtkSharp.Generation { return true; } - protected virtual bool Readable { + internal virtual bool Readable { get { if (Parser.GetVersion (elem.OwnerDocument.DocumentElement) <= 2) return elem.GetAttribute ("readable") != "false"; @@ -48,7 +48,7 @@ namespace GtkSharp.Generation { } } - protected virtual bool Writable { + internal virtual bool Writable { get { if (Parser.GetVersion (elem.OwnerDocument.DocumentElement) <= 2) return elem.GetAttribute ("writeable") != "false"; @@ -58,7 +58,7 @@ namespace GtkSharp.Generation { protected abstract string DefaultAccess { get; } - protected string Access { + internal string Access { get { return elem.HasAttribute ("access") ? elem.GetAttribute ("access") : DefaultAccess; } diff --git a/generator/Makefile.am b/generator/Makefile.am index a45405357..ffea9f0a0 100644 --- a/generator/Makefile.am +++ b/generator/Makefile.am @@ -43,6 +43,7 @@ sources = \ MethodBase.cs \ MethodBody.cs \ Method.cs \ + NativeStructGen.cs \ ObjectField.cs \ ObjectBase.cs \ ObjectGen.cs \ diff --git a/generator/NativeStructGen.cs b/generator/NativeStructGen.cs new file mode 100644 index 000000000..b4cb06d46 --- /dev/null +++ b/generator/NativeStructGen.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; + +namespace GtkSharp.Generation +{ + public class NativeStructGen : HandleBase + { + IList fields = new List (); + bool need_read_native = false; + string native_struct_name; + + public NativeStructGen (XmlElement ns, XmlElement elem) : base (ns, elem) + { + native_struct_name = Name + "Impl"; + + foreach (XmlNode node in elem.ChildNodes) { + + if (!(node is XmlElement)) continue; + XmlElement member = (XmlElement) node; + + switch (node.Name) { + case "field": + fields.Add (new StructField (member, this)); + break; + + default: + if (!IsNodeNameHandled (node.Name)) + Console.WriteLine ("Unexpected node " + node.Name + " in " + CName); + break; + } + } + } + + public override string MarshalType { + get { + return "IntPtr"; + } + } + + public override string AssignToName { + get { return "Handle"; } + } + + public override string CallByName () + { + return "Handle"; + } + + public override string CallByName (string var) + { + return String.Format ("{0} == null ? IntPtr.Zero : {0}.{1}", var, "Handle"); + } + + public override string FromNative (string var, bool owned) + { + return "new " + QualifiedName + "( " + var + " )"; + } + + public override void Generate (GenerationInfo gen_info) + { + bool need_close = false; + if (gen_info.Writer == null) { + gen_info.Writer = gen_info.OpenStream (Name); + need_close = true; + } + + StreamWriter sw = gen_info.Writer; + + sw.WriteLine ("namespace " + NS + " {"); + sw.WriteLine (); + sw.WriteLine ("\tusing System;"); + sw.WriteLine ("\tusing System.Collections;"); + sw.WriteLine ("\tusing System.Collections.Generic;"); + sw.WriteLine ("\tusing System.Runtime.InteropServices;"); + sw.WriteLine (); + + sw.WriteLine ("#region Autogenerated code"); + if (IsDeprecated) + sw.WriteLine ("\t[Obsolete]"); + string access = IsInternal ? "internal" : "public"; + sw.WriteLine ("\t" + access + " partial class {0} : IEquatable<{0}> {{", Name); + sw.WriteLine (); + + need_read_native = false; + GenNativeStruct (gen_info); + GenUpdate (gen_info); + GenFields (gen_info); + sw.WriteLine (); + GenCtors (gen_info); + GenMethods (gen_info, null, this); + if (need_read_native) + GenUpdate (gen_info); + GenEqualsAndHash (sw); + + if (!need_close) + return; + + sw.WriteLine ("#endregion"); + + sw.WriteLine ("\t}"); + sw.WriteLine ("}"); + sw.Close (); + gen_info.Writer = null; + } + + private void GenNativeStruct (GenerationInfo gen_info) + { + StreamWriter sw = gen_info.Writer; + + sw.WriteLine ("\t\t[StructLayout(LayoutKind.Sequential)]"); + sw.WriteLine ("\t\tprivate struct {0} {{", native_struct_name); + foreach (StructField field in fields) { + field.Generate (gen_info, "\t\t\t"); + } + sw.WriteLine ("\t\t}"); + sw.WriteLine (); + } + + private void GenUpdate (GenerationInfo gen_info) + { + StreamWriter sw = gen_info.Writer; + + sw.WriteLine ("\t\tprivate void Update ()", QualifiedName); + sw.WriteLine ("\t\t{"); + sw.WriteLine ("\t\t\tthis.managed_struct = ({0})Marshal.PtrToStructure (this.Handle, typeof({0}));", native_struct_name); + sw.WriteLine ("\t\t}"); + sw.WriteLine (); + } + + protected override void GenCtors (GenerationInfo gen_info) + { + StreamWriter sw = gen_info.Writer; + + sw.WriteLine ("\t\tpublic {0} (IntPtr raw)", Name); + sw.WriteLine ("\t\t{"); + sw.WriteLine ("\t\t\tthis.Handle = raw;"); + sw.WriteLine ("\t\t}"); + sw.WriteLine (); + + base.GenCtors (gen_info); + } + + protected new void GenFields (GenerationInfo gen_info) + { + StreamWriter sw = gen_info.Writer; + sw.WriteLine ("\t\tprivate IntPtr Raw;"); + sw.WriteLine ("\t\tpublic IntPtr Handle {"); + sw.WriteLine ("\t\t\tget { return Raw; }"); + sw.WriteLine ("\t\t\tset { Raw = value; Update ();}"); + sw.WriteLine ("\t\t}"); + sw.WriteLine ("\t\tprivate {0} managed_struct;", native_struct_name); + sw.WriteLine (); + foreach (StructField field in fields) { + if (!field.Visible) + continue; + sw.WriteLine ("\t\tpublic {0} {1} {{", SymbolTable.Table.GetCSType (field.CType), field.StudlyName); + sw.WriteLine ("\t\t\tget {{ Update(); return {0}.{1}; }}", "managed_struct", field.StudlyName); + if (!(SymbolTable.Table [field.CType] is CallbackGen)) + sw.WriteLine ("\t\t\tset {{ Update(); {0}.{1} = value; Marshal.StructureToPtr({0}, this.Handle, false); }}" , "managed_struct", field.StudlyName); + sw.WriteLine ("\t\t}"); + } + } + + protected void GenEqualsAndHash (StreamWriter sw) + { + int bitfields = 0; + bool need_field = true; + StringBuilder hashcode = new StringBuilder (); + StringBuilder equals = new StringBuilder (); + + sw.WriteLine ("\t\tpublic bool Equals ({0} other)", Name); + sw.WriteLine ("\t\t{"); + hashcode.Append ("this.GetType().FullName.GetHashCode()"); + equals.Append ("true"); + + foreach (StructField field in fields) { + if (field.IsPadding || !field.Visible) + continue; + if (field.IsBitfield) { + if (need_field) { + equals.Append (" && _bitfield"); + equals.Append (bitfields); + equals.Append (".Equals (other._bitfield"); + equals.Append (bitfields); + equals.Append (")"); + hashcode.Append (" ^ "); + hashcode.Append ("_bitfield"); + hashcode.Append (bitfields++); + hashcode.Append (".GetHashCode ()"); + need_field = false; + } + } else { + need_field = true; + equals.Append (" && "); + equals.Append (field.EqualityName); + equals.Append (".Equals (other."); + equals.Append (field.EqualityName); + equals.Append (")"); + hashcode.Append (" ^ "); + hashcode.Append (field.EqualityName); + hashcode.Append (".GetHashCode ()"); + } + } + sw.WriteLine ("\t\t\treturn {0};", equals.ToString ()); + sw.WriteLine ("\t\t}"); + sw.WriteLine (); + sw.WriteLine ("\t\tpublic override bool Equals (object other)"); + sw.WriteLine ("\t\t{"); + sw.WriteLine ("\t\t\treturn other is {0} && Equals (({0}) other);", Name); + sw.WriteLine ("\t\t}"); + sw.WriteLine (); + if (Elem.GetAttribute ("nohash") == "true") + return; + sw.WriteLine ("\t\tpublic override int GetHashCode ()"); + sw.WriteLine ("\t\t{"); + sw.WriteLine ("\t\t\treturn {0};", hashcode.ToString ()); + sw.WriteLine ("\t\t}"); + sw.WriteLine (); + + } + + public override void Prepare (StreamWriter sw, string indent) + { + sw.WriteLine (indent + "Update ();"); + } + } +} + diff --git a/generator/ObjectField.cs b/generator/ObjectField.cs index f0edd1a5e..2b004a6ff 100644 --- a/generator/ObjectField.cs +++ b/generator/ObjectField.cs @@ -32,7 +32,7 @@ namespace GtkSharp.Generation { ctype = "const-" + CType; } - protected override bool Writable { + internal override bool Writable { get { return elem.GetAttributeAsBoolean ("writeable"); } diff --git a/generator/Parser.cs b/generator/Parser.cs index 0974d5908..0d96e59fd 100644 --- a/generator/Parser.cs +++ b/generator/Parser.cs @@ -140,6 +140,7 @@ namespace GtkSharp.Generation { continue; bool is_opaque = elem.GetAttributeAsBoolean ("opaque"); + bool is_native_struct = elem.GetAttributeAsBoolean ("native"); switch (def.Name) { case "alias": @@ -177,6 +178,8 @@ namespace GtkSharp.Generation { case "struct": if (is_opaque) { result.Add (new OpaqueGen (ns, elem)); + } else if (is_native_struct) { + result.Add (new NativeStructGen (ns, elem)); } else { result.Add (new StructGen (ns, elem)); } diff --git a/generator/StructField.cs b/generator/StructField.cs index e0ba545d4..d60273cb0 100644 --- a/generator/StructField.cs +++ b/generator/StructField.cs @@ -70,6 +70,13 @@ namespace GtkSharp.Generation { } } + bool visible = false; + internal bool Visible { + get { + return visible; + } + } + public string EqualityName { get { SymbolTable table = SymbolTable.Table; @@ -127,6 +134,8 @@ namespace GtkSharp.Generation { if (Hidden) return; + visible = Access != "private"; + StreamWriter sw = gen_info.Writer; SymbolTable table = SymbolTable.Table; @@ -158,6 +167,7 @@ namespace GtkSharp.Generation { } } else if (IsPointer && CSType != "string") { // FIXME: probably some fields here which should be visible. + visible = false; sw.WriteLine (indent + "private {0} {1};", CSType, Name); } else { sw.WriteLine (indent + "{0} {1} {2};", Access, CSType, Access == "public" ? StudlyName : Name); diff --git a/generator/generator.csproj b/generator/generator.csproj index 1d19e62ae..681863983 100644 --- a/generator/generator.csproj +++ b/generator/generator.csproj @@ -93,6 +93,7 @@ + @@ -102,4 +103,4 @@ - \ No newline at end of file + diff --git a/glib/Value.cs b/glib/Value.cs index deb7deb3e..1c89cc37d 100755 --- a/glib/Value.cs +++ b/glib/Value.cs @@ -421,10 +421,14 @@ namespace GLib { return (GLib.Opaque) this; MethodInfo mi = t.GetMethod ("New", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy); - if (mi == null) - return Marshal.PtrToStructure (boxed_ptr, t); - else + if (mi != null) return mi.Invoke (null, new object[] {boxed_ptr}); + + ConstructorInfo ci = t.GetConstructor (new Type[] { typeof(IntPtr) }); + if (ci != null) + return ci.Invoke (new object[] { boxed_ptr }); + + return Marshal.PtrToStructure (boxed_ptr, t); } public object Val @@ -548,8 +552,15 @@ namespace GLib { internal void Update (object val) { - if (GType.Is (type, GType.Boxed) && !(val is IWrapper)) - Marshal.StructureToPtr (val, g_value_get_boxed (ref this), false); + Type t = GType.LookupType (type); + if (GType.Is (type, GType.Boxed) && !(val is IWrapper)) { + MethodInfo mi = val.GetType ().GetMethod ("Update", BindingFlags.NonPublic | BindingFlags.Instance); + IntPtr boxed_ptr = g_value_get_boxed (ref this); + if (mi == null) + Marshal.StructureToPtr (val, boxed_ptr, false); + else + mi.Invoke (val, null); + } } bool HoldsFlags {