Generate ABI compatible structures to avoid needing glue code.

This commit is contained in:
Thibault Saunier 2017-08-31 08:45:26 -03:00
parent 44d2af717a
commit 4d513324cd
15 changed files with 259 additions and 24 deletions

View file

@ -39,6 +39,9 @@ namespace GtkSharp.Generation {
protected IList<string> managed_interfaces = new List<string>();
protected IList<Ctor> ctors = new List<Ctor>();
protected IList<StructABIField> abi_fields = new List<StructABIField> ();
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<string, Ctor> 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) {

View file

@ -108,6 +108,7 @@ PropertyBase: Abstract base class for property-like elements
FieldBase: Abstract base class for field-like elements
ObjectField: Handles <field> elements in objects
StructField: Handles <field> elements in structs
StructABIField: Handles <fields> to generate ABI compatible structures
ClassField: Handles <field> elements in classes
Property: Handles <property> elements
ChildProperty: Handles <childprop> elements

View file

@ -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);
}

View file

@ -65,6 +65,7 @@ sources = \
SimpleGen.cs \
Statistics.cs \
StructBase.cs \
StructABIField.cs \
StructField.cs \
StructGen.cs \
SymbolTable.cs \

View file

@ -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;

View file

@ -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;
}

View file

@ -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 {");

View file

@ -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);

View file

@ -72,7 +72,7 @@ namespace GtkSharp.Generation {
}
}
public bool Hidden {
public virtual bool Hidden {
get {
return elem.GetAttributeAsBoolean ("hidden");
}

View file

@ -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;
}
}
}

View file

@ -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";

View file

@ -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; }");

View file

@ -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)

View file

@ -41,6 +41,7 @@ gapi_codegen = executable('gapi_codegen',
'Method.cs',
'NativeStructGen.cs',
'ObjectField.cs',
'StructABIField.cs',
'ObjectBase.cs',
'ObjectGen.cs',
'OpaqueGen.cs',

View file

@ -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;