diff --git a/generator/ClassBase.cs b/generator/ClassBase.cs index 57e277044..f2dee2630 100644 --- a/generator/ClassBase.cs +++ b/generator/ClassBase.cs @@ -39,6 +39,9 @@ namespace GtkSharp.Generation { protected IList managed_interfaces = new List(); protected IList ctors = new List(); + protected IList abi_fields = new List (); + protected bool abi_fields_valid; // false if the instance structure contains a bitfield or fields of unknown types + private bool ctors_initted = false; private Dictionary clash_map; private bool deprecated = false; @@ -71,10 +74,24 @@ namespace GtkSharp.Generation { deprecated = elem.GetAttributeAsBoolean ("deprecated"); isabstract = elem.GetAttributeAsBoolean ("abstract"); + abi_fields_valid = true; + bool has_parent = Elem.GetAttribute("parent") != ""; + int num_abi_fields = 0; foreach (XmlNode node in elem.ChildNodes) { if (!(node is XmlElement)) continue; XmlElement member = (XmlElement) node; + StructABIField abi_field = null; + + // Make sure ABI fields are taken into account, even when hidden. + if (node.Name == "field") { + num_abi_fields += 1; + if (num_abi_fields != 1 || !has_parent) { // Skip instance parent struct + abi_field = new StructABIField (member, this); + abi_fields.Add (abi_field); + } + } + if (member.GetAttributeAsBoolean ("hidden")) continue; @@ -98,7 +115,10 @@ namespace GtkSharp.Generation { name = member.GetAttribute("name"); while (fields.ContainsKey (name)) name += "mangled"; - fields.Add (name, new ObjectField (member, this)); + + var field = new ObjectField (member, this); + field.abi_field = abi_field; + fields.Add (name, field); break; case "implements": @@ -120,6 +140,89 @@ namespace GtkSharp.Generation { } } + protected virtual bool CanGenerateABIStruct { + get { + return (abi_fields_valid); + } + } + + bool CheckABIStructParent(LogWriter log, out string cs_parent_struct) { + cs_parent_struct = null; + if (!CanGenerateABIStruct) + return false; + + var parent = SymbolTable.Table[Elem.GetAttribute("parent")]; + string cs_parent = SymbolTable.Table.GetCSType(Elem.GetAttribute("parent")); + var parent_can_generate = true; + + cs_parent_struct = null; + if (parent != null) { + // FIXME Add that information to ManualGen and use it. + if (parent.CName == "GInitiallyUnowned" || parent.CName == "GObject") { + cs_parent_struct = "GLib.Object.GObject"; + } else { + parent_can_generate = false; + var _parent = parent as ClassBase; + + if (_parent != null) { + string tmp; + parent_can_generate = _parent.CheckABIStructParent(log, out tmp); + } + + if (parent_can_generate) { + cs_parent_struct = cs_parent + "._" + parent.CName + "ABI"; + } + } + + if (!parent_can_generate) { + log.Warn("Can't generate ABI structrure as the parent structure '" + + parent.CName + "' can't be generated."); + return false; + } + } else { + cs_parent_struct = ""; + } + return parent_can_generate; + } + + protected virtual void WriteInstanceOffsetMethod(StreamWriter sw, + string cs_parent_struct) { + if (cs_parent_struct == "") + sw.WriteLine ("\t\tpublic virtual uint instance_offset { get { return 0; }}"); + else + sw.WriteLine ("\t\tpublic override uint instance_offset {{ get {{ return ((uint) Marshal.SizeOf(typeof ({0})) + base.instance_offset); }} }}", cs_parent_struct); + } + + protected void GenerateStructureABI (GenerationInfo gen_info) + { + string cs_parent_struct = null; + + LogWriter log = new LogWriter (QualifiedName); + if (!CheckABIStructParent (log, out cs_parent_struct)) + return; + + StreamWriter sw = gen_info.Writer; + + var _new = ""; + if (cs_parent_struct != "") + _new = "new"; + + WriteInstanceOffsetMethod(sw, cs_parent_struct); + sw.WriteLine (); + sw.WriteLine ("\t\tpublic " + _new + " unsafe uint GetFieldOffset(string field) {"); + sw.WriteLine ("\t\t\treturn (uint) (instance_offset + (uint) Marshal.OffsetOf(typeof({0}), field));", + (QualifiedName + "._" + CName + "ABI")); + sw.WriteLine ("\t\t}"); + sw.WriteLine (); + + sw.WriteLine ("\t\t[StructLayout (LayoutKind.Sequential)]"); + sw.WriteLine ("\t\tpublic struct _" + CName + "ABI" + " {"); + foreach (StructABIField field in abi_fields) + field.Generate (gen_info, "\t\t\t"); + sw.WriteLine ("\t\t}"); + sw.WriteLine (); + } + public override bool Validate () { LogWriter log = new LogWriter (QualifiedName); @@ -136,6 +239,11 @@ namespace GtkSharp.Generation { } } + foreach (StructABIField abi_field in abi_fields) { + if (!abi_field.Validate(log)) + abi_fields_valid = false; + } + ArrayList invalids = new ArrayList (); foreach (Property prop in props.Values) { diff --git a/generator/DESIGN b/generator/DESIGN index de4d4e346..616b6d778 100644 --- a/generator/DESIGN +++ b/generator/DESIGN @@ -108,6 +108,7 @@ PropertyBase: Abstract base class for property-like elements FieldBase: Abstract base class for field-like elements ObjectField: Handles elements in objects StructField: Handles elements in structs + StructABIField: Handles to generate ABI compatible structures ClassField: Handles elements in classes Property: Handles elements ChildProperty: Handles elements diff --git a/generator/FieldBase.cs b/generator/FieldBase.cs index da5954524..dbe962125 100644 --- a/generator/FieldBase.cs +++ b/generator/FieldBase.cs @@ -26,7 +26,13 @@ namespace GtkSharp.Generation { using System.Xml; public abstract class FieldBase : PropertyBase { + public FieldBase abi_field = null; + public FieldBase (XmlElement elem, ClassBase container_type) : base (elem, container_type) {} + public FieldBase (XmlElement elem, ClassBase container_type, FieldBase abi_field) : base (elem, container_type) { + abi_field = abi_field; + } + public virtual bool Validate (LogWriter log) { @@ -60,7 +66,7 @@ namespace GtkSharp.Generation { protected abstract string DefaultAccess { get; } - internal string Access { + protected virtual string Access { get { return elem.HasAttribute ("access") ? elem.GetAttribute ("access") : DefaultAccess; } @@ -90,15 +96,30 @@ namespace GtkSharp.Generation { } } - string getterName, setterName; - string getOffsetName, offsetName; + private bool UseABIStruct(GenerationInfo gen_info) { + return (abi_field != null && abi_field.getOffsetName != null && + gen_info.GlueWriter == null); + } - void CheckGlue () + string getterName, setterName; + protected string getOffsetName, offsetName; + + void CheckGlue (GenerationInfo gen_info) { getterName = setterName = getOffsetName = null; if (Access != "public") return; + if (UseABIStruct(gen_info)) { + getOffsetName = abi_field.getOffsetName; + offsetName = "GetFieldOffset(\"" + ((StructField)abi_field).EqualityName + "\")"; + + return; + } + + if (gen_info.GlueWriter == null) + return; + string prefix = (container_type.NS + "Sharp_" + container_type.NS + "_" + container_type.Name).Replace(".", "__").ToLower (); if (IsBitfield) { @@ -119,17 +140,22 @@ namespace GtkSharp.Generation { StreamWriter sw = gen_info.Writer; SymbolTable table = SymbolTable.Table; + if (gen_info.GlueWriter == null) { + base.GenerateImports(gen_info, indent); + return; + } + if (getterName != null) { sw.WriteLine (indent + "[DllImport (\"{0}\")]", gen_info.GluelibName); sw.WriteLine (indent + "extern static {0} {1} ({2} raw);", - table.GetMarshalType (CType), getterName, - container_type.MarshalType); + table.GetMarshalType (CType), getterName, + container_type.MarshalType); } if (setterName != null) { sw.WriteLine (indent + "[DllImport (\"{0}\")]", gen_info.GluelibName); sw.WriteLine (indent + "extern static void {0} ({1} raw, {2} value);", - setterName, container_type.MarshalType, table.GetMarshalType (CType)); + setterName, container_type.MarshalType, table.GetMarshalType (CType)); } if (getOffsetName != null) { @@ -147,15 +173,15 @@ namespace GtkSharp.Generation { if (Ignored || Hidden) return; - CheckGlue (); - if ((getterName != null || setterName != null || getOffsetName != null) && gen_info.GlueWriter == null) { - LogWriter log = new LogWriter (container_type.QualifiedName); - log.Member = Name; - log.Warn ("needs glue for field access. Specify --glue-filename"); + CheckGlue (gen_info); + + GenerateImports (gen_info, indent); + + if (Getter == null && getterName == null && offsetName == null && + Setter == null && setterName == null) { return; } - GenerateImports (gen_info, indent); SymbolTable table = SymbolTable.Table; IGeneratable gen = table [CType]; @@ -222,7 +248,7 @@ namespace GtkSharp.Generation { sw.WriteLine (indent + "}"); sw.WriteLine (""); - if (getterName != null || setterName != null || getOffsetName != null) + if ((getterName != null || setterName != null || getOffsetName != null) && gen_info.GlueWriter != null) GenerateGlue (gen_info); } diff --git a/generator/Makefile.am b/generator/Makefile.am index bdfae896c..388cd011e 100644 --- a/generator/Makefile.am +++ b/generator/Makefile.am @@ -65,6 +65,7 @@ sources = \ SimpleGen.cs \ Statistics.cs \ StructBase.cs \ + StructABIField.cs \ StructField.cs \ StructGen.cs \ SymbolTable.cs \ diff --git a/generator/ObjectBase.cs b/generator/ObjectBase.cs index d8965f8a4..2a8b6c18e 100644 --- a/generator/ObjectBase.cs +++ b/generator/ObjectBase.cs @@ -187,6 +187,21 @@ namespace GtkSharp.Generation { } } + protected override bool CanGenerateABIStruct { + get { + if (!abi_fields_valid) { + Console.WriteLine("invalid fields"); + return false; + } + + // No instance structure for interfaces + if (is_interface) + return false; + + return class_struct_name != null; + } + } + protected void GenerateClassStruct (GenerationInfo gen_info) { if (class_struct_name == null || !CanGenerateClassStruct) return; diff --git a/generator/ObjectField.cs b/generator/ObjectField.cs index 2b004a6ff..dca196c46 100644 --- a/generator/ObjectField.cs +++ b/generator/ObjectField.cs @@ -26,8 +26,12 @@ namespace GtkSharp.Generation { public class ObjectField : FieldBase { - public ObjectField (XmlElement elem, ClassBase container_type) : base (elem, container_type) - { + public ObjectField (XmlElement elem, ClassBase container_type) : base (elem, container_type){ + if (CType == "char*" || CType == "gchar*") + ctype = "const-" + CType; + } + + public ObjectField (XmlElement elem, ClassBase container_type, FieldBase abi_field) : base (elem, container_type) { if (CType == "char*" || CType == "gchar*") ctype = "const-" + CType; } diff --git a/generator/ObjectGen.cs b/generator/ObjectGen.cs index d5812073b..f355663f7 100644 --- a/generator/ObjectGen.cs +++ b/generator/ObjectGen.cs @@ -118,7 +118,7 @@ namespace GtkSharp.Generation { if (dirs.ContainsKey (dir)) { result = dirs [dir]; - if (result.assembly_name != assembly_name) { + if (result.assembly_name != assembly_name) { Console.WriteLine ("Can't put multiple assemblies in one directory."); return null; } @@ -175,6 +175,7 @@ namespace GtkSharp.Generation { sw.WriteLine (" {"); sw.WriteLine (); + GenerateStructureABI (gen_info); GenCtors (gen_info); GenProperties (gen_info, null); GenFields (gen_info); @@ -285,7 +286,7 @@ namespace GtkSharp.Generation { ObjectGen child_ancestor = Parent as ObjectGen; while (child_ancestor.CName != "GtkContainer" && - child_ancestor.childprops.Count == 0) + child_ancestor.childprops.Count == 0) child_ancestor = child_ancestor.Parent as ObjectGen; sw.WriteLine ("\t\tpublic class " + Name + "Child : " + child_ancestor.NS + "." + child_ancestor.Name + "." + child_ancestor.Name + "Child {"); diff --git a/generator/OpaqueGen.cs b/generator/OpaqueGen.cs index 7ca2cc443..24fd4b3bf 100644 --- a/generator/OpaqueGen.cs +++ b/generator/OpaqueGen.cs @@ -80,6 +80,7 @@ namespace GtkSharp.Generation { sw.WriteLine (" {"); sw.WriteLine (); + GenerateStructureABI(gen_info); GenConstants (gen_info); GenFields (gen_info); GenMethods (gen_info, null, null); diff --git a/generator/PropertyBase.cs b/generator/PropertyBase.cs index a673b7100..b2cc1bb48 100644 --- a/generator/PropertyBase.cs +++ b/generator/PropertyBase.cs @@ -72,7 +72,7 @@ namespace GtkSharp.Generation { } } - public bool Hidden { + public virtual bool Hidden { get { return elem.GetAttributeAsBoolean ("hidden"); } diff --git a/generator/StructABIField.cs b/generator/StructABIField.cs new file mode 100644 index 000000000..251c4ab3d --- /dev/null +++ b/generator/StructABIField.cs @@ -0,0 +1,57 @@ +namespace GtkSharp.Generation { + + using System; + using System.Collections; + using System.IO; + using System.Xml; + + public class StructABIField : StructField { + protected new ClassBase container_type; + + public StructABIField (XmlElement elem, ClassBase container_type) : base (elem, container_type) { + this.container_type = container_type; + this.getOffsetName = null; + } + + public override void Generate (GenerationInfo gen_info, string indent) { + this.getOffsetName = "Get" + CName + "Offset"; + base.Generate(gen_info, indent); + } + + // All field are visible and private + // as the goal is to respect the ABI + protected override string Access { + get { + return "private"; + } + } + + public override bool Hidden { + get { + return false; + } + } + + public override bool Validate (LogWriter log) + { + string cstype = SymbolTable.Table.GetCSType(CType, true); + + if (cstype == null || cstype == "") { + Console.WriteLine("(" + container_type.CName + ") VOOM " + CName + " " + CType + "=> " + cstype); + + return false; + } + + if (!base.Validate (log)) + return false; + + if (IsBitfield) { + log.Warn ("bitfields are not supported"); + return false; + } + + return true; + } + } +} + diff --git a/generator/StructBase.cs b/generator/StructBase.cs index e5ed8fc69..dbcbc62bc 100644 --- a/generator/StructBase.cs +++ b/generator/StructBase.cs @@ -60,6 +60,13 @@ namespace GtkSharp.Generation { } } + protected override void WriteInstanceOffsetMethod(StreamWriter sw, string cs_parent_struct) { + if (cs_parent_struct == "") + sw.WriteLine ("\t\tpublic uint instance_offset { get { return 0; }}"); + else + sw.WriteLine ("\t\tpublic override uint instance_offset {{ get {{ return ((uint) Marshal.SizeOf(typeof ({0})) + base.instance_offset); }} }}", cs_parent_struct); + } + public override string DefaultValue { get { return QualifiedName + ".Zero"; diff --git a/generator/StructGen.cs b/generator/StructGen.cs index 84c71006b..0be2d0a06 100644 --- a/generator/StructGen.cs +++ b/generator/StructGen.cs @@ -35,6 +35,7 @@ namespace GtkSharp.Generation { StreamWriter sw = gen_info.Writer = gen_info.OpenStream (Name, NS); base.Generate (gen_info); + GenerateStructureABI(gen_info); if (GetMethod ("GetType") == null && GetMethod ("GetGType") == null) { sw.WriteLine ("\t\tprivate static GLib.GType GType {"); sw.WriteLine ("\t\t\tget { return GLib.GType.Pointer; }"); diff --git a/generator/SymbolTable.cs b/generator/SymbolTable.cs index 212ec818c..3a7da9cc9 100644 --- a/generator/SymbolTable.cs +++ b/generator/SymbolTable.cs @@ -245,12 +245,22 @@ namespace GtkSharp.Generation { return gen.FromNative (val); } - public string GetCSType(string c_type) + public string GetCSType(string c_type, bool default_pointer) { IGeneratable gen = this[c_type]; - if (gen == null) - return ""; - return gen.QualifiedName; + if (gen == null) { + if (c_type.EndsWith("*") && default_pointer) + return "UintPtr"; + + return ""; + } + + return gen.QualifiedName; + } + + public string GetCSType(string c_type) + { + return GetCSType(c_type, false); } public string GetName(string c_type) diff --git a/generator/meson.build b/generator/meson.build index 3f0e2cd5c..f42add2d6 100644 --- a/generator/meson.build +++ b/generator/meson.build @@ -41,6 +41,7 @@ gapi_codegen = executable('gapi_codegen', 'Method.cs', 'NativeStructGen.cs', 'ObjectField.cs', + 'StructABIField.cs', 'ObjectBase.cs', 'ObjectGen.cs', 'OpaqueGen.cs', diff --git a/glib/Object.cs b/glib/Object.cs index 4c1ebfa1b..3e995ac1f 100644 --- a/glib/Object.cs +++ b/glib/Object.cs @@ -823,6 +823,8 @@ namespace GLib { public IntPtr g_class; } + public virtual uint instance_offset { get { return 0; }} + public uint GetFieldOffset(string field) { throw new NotImplementedException(); return 0; } public struct GObject { public GTypeInstance type_instance; public uint ref_count;