Emit native signatures

The patcher uses those signatures to implement wrapper functionality
for the various wrapper types that exist in OpenTK.
This commit is contained in:
Stefanos A 2013-11-27 00:02:26 +01:00
parent bb7980b6a9
commit 82b0b477da

View file

@ -328,7 +328,7 @@ namespace Bind
sw.WriteLine("}");
sw.WriteLine();
int current = 0;
int current_wrapper = 0;
foreach (string key in wrappers.Keys)
{
if (((Settings.Compatibility & Settings.Legacy.NoSeparateFunctionNamespaces) == Settings.Legacy.None) && key != "Core")
@ -349,7 +349,8 @@ namespace Bind
wrappers[key].Sort();
foreach (Function f in wrappers[key])
{
current = WriteWrapper(sw, current, f, enums);
WriteWrapper(sw, f, enums);
current_wrapper++;
}
if (((Settings.Compatibility & Settings.Legacy.NoSeparateFunctionNamespaces) == Settings.Legacy.None) && key != "Core")
@ -359,28 +360,35 @@ namespace Bind
sw.WriteLine();
}
}
// Emit native signatures.
// These are required by the patcher.
int current_signature = 0;
foreach (var d in wrappers.Values.SelectMany(e => e).Select(w => w.WrappedDelegate).Distinct())
{
sw.WriteLine("[Slot({0})]", d.Slot);
sw.WriteLine("static extern {0};", GetDeclarationString(d, false));
current_signature++;
}
sw.Unindent();
sw.WriteLine("}");
Console.WriteLine("Wrote {0} wrappers for {1} signatures", current_wrapper, current_signature);
}
int WriteWrapper(BindStreamWriter sw, int current, Function f, EnumCollection enums)
void WriteWrapper(BindStreamWriter sw, Function f, EnumCollection enums)
{
if ((Settings.Compatibility & Settings.Legacy.NoDocumentation) == 0)
{
string text = String.Format("Writing function #{0}: {1}", current++, f.ToString());
ConsoleRewrite(text);
WriteDocumentation(sw, f);
}
WriteMethod(sw, f, enums);
sw.WriteLine();
return current;
}
private void WriteMethod(BindStreamWriter sw, Function f, EnumCollection enums)
{
CreateBody(f, enums);
if (!String.IsNullOrEmpty(f.Obsolete))
{
sw.WriteLine("[Obsolete(\"{0}\")]", f.Obsolete);
@ -397,10 +405,7 @@ namespace Bind
sw.WriteLine("[AutoGenerated(Category = \"{0}\", Version = \"{1}\", EntryPoint = \"{2}\")]",
f.Category, f.Version, Settings.FunctionPrefix + f.WrappedDelegate.EntryPoint);
sw.WriteLine("[Slot({0})]", f.WrappedDelegate.Slot);
sw.WriteLine("public static extern ");
sw.Write(GetDeclarationString(f));
sw.WriteLine();
sw.WriteLine("public static extern {0};", GetDeclarationString(f));
}
DocProcessor processor_;
@ -565,9 +570,6 @@ namespace Bind
int current = 0;
foreach (Enum @enum in enums.Values)
{
string text = String.Format("Writing enum #{0}: {1}", current++, @enum.Name);
ConsoleRewrite(text);
if (!Settings.IsEnabled(Settings.Legacy.NoDocumentation))
{
// Document which functions use this enum.
@ -650,254 +652,6 @@ namespace Bind
return enums.ContainsKey(s);
}
void CreateBody(Function func, EnumCollection enums)
{
Function f = new Function(func);
f.Body.Clear();
var pin_statements = new List<string>();
var assign_statements = new List<string>();
var declaration_statements = new List<string>();
// Obtain pointers by pinning the parameters
int index = -1;
foreach (Parameter p in f.Parameters)
{
index++;
if (p.NeedsPin)
{
if (p.WrapperType == WrapperTypes.GenericParameter ||
p.WrapperType == WrapperTypes.PointerParameter ||
p.WrapperType == WrapperTypes.ArrayParameter ||
p.WrapperType == WrapperTypes.ReferenceParameter)
{
// Pin the parameter to obtain a pointer we can safely pass to unmanaged code
if (p.Pointer > 0)
{
declaration_statements.Add(String.Format("IntPtr {0}_ptr = new IntPtr({0});", p.Name));
}
pin_statements.Add(String.Format(
"{2}{0}_ptr = InteropHelper.Pin({1}{0});",
p.Name,
p.Reference ? "ref " : "",
p.Pointer == 0 ? "IntPtr " : ""));
// We also need to initialize out parameters, in order to make the compiler happy
if (p.Flow == FlowDirection.Out && p.Reference)
{
declaration_statements.Add(String.Format("{0} = default({1});", p.Name, p.QualifiedType));
}
}
else if (p.WrapperType == WrapperTypes.None)
{
// do nothing
}
else
{
throw new ApplicationException(String.Format(
"Unknown wrapper type '{0}', code generation failed",
p.WrapperType));
}
}
else if (p.WrapperType == WrapperTypes.ConvenienceArrayType)
{
var p_array = f.WrappedDelegate.Parameters[f.WrappedDelegate.Parameters.Count - 1];
var p_size = f.WrappedDelegate.Parameters[f.WrappedDelegate.Parameters.Count - 2];
declaration_statements.Add(String.Format(
"const {0} = 1;",
GetDeclarationString(p_size, false)));
declaration_statements.Add(String.Format("{0}_ptr = ({1})&{2};",
GetDeclarationString(p_array, false),
GetDeclarationString(p_array as Type),
p.Name));
}
p.QualifiedType = f.WrappedDelegate.Parameters[index].QualifiedType;
}
if (f.ReturnType.WrapperType == WrapperTypes.ConvenienceReturnType ||
f.ReturnType.WrapperType == WrapperTypes.ConvenienceArrayReturnType)
{
var r = f.ReturnType;
var p = f.WrappedDelegate.Parameters.Last();
if (r.WrapperType == WrapperTypes.ConvenienceArrayReturnType)
{
var p_size = f.WrappedDelegate.Parameters[f.WrappedDelegate.Parameters.Count - 2];
declaration_statements.Add(String.Format(
"const {0} = 1;",
GetDeclarationString(p_size, false)));
}
declaration_statements.Add(String.Format("{0} retval;", GetDeclarationString(r)));
declaration_statements.Add(String.Format("{0}{2} {1}_ptr = &retval;",
GetDeclarationString(r),
p.Name,
pointer_levels[p.IndirectionLevel]));
}
f.Body.Indent();
// Automatic OpenGL error checking.
// See OpenTK.Graphics.ErrorHelper for more information.
// Make sure that no error checking is added to the GetError function,
// as that would cause infinite recursion!
if ((Settings.Compatibility & Settings.Legacy.NoDebugHelpers) == 0)
{
if (f.TrimmedName != "GetError")
{
f.Body.Add("#if DEBUG");
f.Body.Add("using (new ErrorHelper(GraphicsContext.CurrentContext))");
f.Body.Add("{");
if (f.TrimmedName == "Begin")
f.Body.Add("GraphicsContext.CurrentContext.ErrorChecking = false;");
f.Body.Add("#endif");
}
}
// Mark the body as unsafe if necessary
bool add_unsafe = !f.Unsafe && declaration_statements.Count > 0;
if (add_unsafe)
{
f.Body.Add("unsafe");
f.Body.Add("{");
f.Body.Indent();
}
if (declaration_statements.Count > 0)
{
f.Body.AddRange(declaration_statements);
}
if (pin_statements.Count > 0)
{
f.Body.AddRange(pin_statements);
}
// Hack: When creating untyped enum wrappers, it is possible that the wrapper uses an "All"
// enum, while the delegate uses a specific enum (e.g. "TextureUnit"). For this reason, we need
// to modify the parameters before generating the call string.
// Note: We cannot generate a callstring using WrappedDelegate directly, as its parameters will
// typically be different than the parameters of the wrapper. We need to modify the parameters
// of the wrapper directly.
var wrapped = new Delegate(f.WrappedDelegate);
if ((Settings.Compatibility & Settings.Legacy.KeepUntypedEnums) != 0)
{
int parameter_index = -1; // Used for comparing wrapper parameters with delegate parameters
foreach (Parameter p in f.Parameters)
{
parameter_index++;
if (IsEnum(p.Name, enums) && p.QualifiedType != wrapped.Parameters[parameter_index].QualifiedType)
{
p.QualifiedType = wrapped.Parameters[parameter_index].QualifiedType;
}
}
}
if (assign_statements.Count > 0)
{
// Call function
var callstring = GetInvocationString(f, true);
if (func.Parameters.Any(p => p.WrapperType == WrapperTypes.ConvenienceArrayType))
{
// foo(int id) { foo(1, ref id) }
callstring = GetInvocationString(wrapped, true);
f.Body.Add(String.Format("{0}{1};",
f.ReturnType.CurrentType.ToLower().Contains("void") ? String.Empty : "return ",
callstring));
}
else if (f.ReturnType.CurrentType.ToLower().Contains("void"))
{
f.Body.Add(String.Format("{0};", callstring));
}
else if (func.ReturnType.WrapperType == WrapperTypes.ConvenienceReturnType ||
func.ReturnType.WrapperType == WrapperTypes.ConvenienceArrayReturnType)
{
// int foo() { int value; foo(1, &value); retval = value }
callstring = GetInvocationString(wrapped, true);
var p = wrapped.Parameters.Last();
f.Body.Add(String.Format("{0};", callstring));
f.Body.Add(String.Format(
"retval = {0}{1};",
pointer_levels[p.IndirectionLevel],
p.Name));
}
else if (func.ReturnType.CurrentType.ToLower().Contains("string"))
{
f.Body.Add(String.Format("{0} {1} = null; unsafe {{ {1} = new string((sbyte*){2}); }}",
func.ReturnType.QualifiedType, "retval", callstring));
}
else
{
f.Body.Add(String.Format("{0} {1} = {2};",
GetDeclarationString(f.ReturnType), "retval", callstring));
}
// Assign out parameters
f.Body.AddRange(assign_statements);
// Return
if (!f.ReturnType.CurrentType.ToLower().Contains("void"))
{
f.Body.Add("return retval;");
}
}
else
{
// Call function and return
var callstring = GetInvocationString(f, true);
if (func.Parameters.Any(p => p.WrapperType == WrapperTypes.ConvenienceArrayType))
{
// int foo(int id) { return foo(1, ref id) }
callstring = GetInvocationString(wrapped, true);
f.Body.Add(String.Format("{0}{1};",
f.ReturnType.CurrentType.ToLower().Contains("void") ? String.Empty : "return ",
callstring));
}
else if (f.ReturnType.CurrentType.ToLower().Contains("void"))
{
f.Body.Add(String.Format("{0};", callstring));
}
else if (func.ReturnType.WrapperType == WrapperTypes.ConvenienceReturnType ||
func.ReturnType.WrapperType == WrapperTypes.ConvenienceArrayReturnType)
{
// int foo() { int retval; foo(1, &retval); return retval }
callstring = GetInvocationString(wrapped, true);
f.Body.Add(String.Format("{0};", callstring));
f.Body.Add(String.Format("return retval;"));
}
else if (func.ReturnType.CurrentType.ToLower().Contains("string"))
{
f.Body.Add(String.Format("unsafe {{ return new string((sbyte*){0}); }}",
callstring));
}
else
{
f.Body.Add(String.Format("return {0};", callstring));
}
}
if (add_unsafe)
{
f.Body.Unindent();
f.Body.Add("}");
}
if ((Settings.Compatibility & Settings.Legacy.NoDebugHelpers) == 0)
{
if (f.TrimmedName != "GetError")
{
f.Body.Add("#if DEBUG");
if (f.TrimmedName == "End")
f.Body.Add("GraphicsContext.CurrentContext.ErrorChecking = true;");
f.Body.Add("}");
f.Body.Add("#endif");
}
}
f.Body.Unindent();
func.Body = f.Body;
}
string GetDeclarationString(Constant c)
{
if (String.IsNullOrEmpty(c.Name))
@ -923,6 +677,7 @@ namespace Bind
sb.Append("delegate ");
sb.Append(GetDeclarationString(d.ReturnType));
sb.Append(" ");
sb.Append(Settings.FunctionPrefix);
sb.Append(d.Name);
sb.Append(GetDeclarationString(d.Parameters));
@ -989,9 +744,12 @@ namespace Bind
sb.Remove(sb.Length - 1, 1);
sb.Append(">");
}
sb.AppendLine(GetDeclarationString(f.Parameters));
sb.Append(GetDeclarationString(f.Parameters));
if (f.Parameters.HasGenericParameters)
{
sb.AppendLine();
foreach (Parameter p in f.Parameters)
{
if (p.Generic)
@ -999,8 +757,6 @@ namespace Bind
}
}
sb.AppendLine(";");
return sb.ToString();
}