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:
parent
bb7980b6a9
commit
82b0b477da
1 changed files with 23 additions and 267 deletions
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue