Add ErrorHelper to bindings.

Wrap new generated binding calls with using ErrorHelper. Sets up a try
to call the method in and a finally block to call Dispose on the
ErrorHelper. Currently hardcoded to only work for the graphics modules.
This commit is contained in:
Fraser 2014-02-20 20:12:38 +00:00 committed by thefiddler
parent efc59fa97a
commit 10ca14ac6d
2 changed files with 158 additions and 13 deletions

View file

@ -34,14 +34,17 @@ namespace OpenTK.Rewrite
{
if (args.Length == 0)
{
Console.WriteLine("Usage: rewrite [file.dll] [file.snk]");
Console.WriteLine("Usage: rewrite [file.dll] [file.snk] [options]");
Console.WriteLine("[options] is:");
Console.WriteLine(" -debug (enable calls to GL.GetError())");
return;
}
var program = new Program();
var file = args[0];
var key = args.Length >= 2 ? args[1] : null;
program.Rewrite(file, key);
var key = args[1];
var options = args.Where(a => a.StartsWith("-") || a.StartsWith("/"));
program.Rewrite(file, key, options);
}
// mscorlib types
@ -55,7 +58,7 @@ namespace OpenTK.Rewrite
// OpenTK.BindingsBase
static TypeDefinition TypeBindingsBase;
void Rewrite(string file, string keyfile)
void Rewrite(string file, string keyfile, IEnumerable<string> options)
{
// Specify assembly read and write parameters
// We want to keep a valid symbols file (pdb or mdb)
@ -123,7 +126,7 @@ namespace OpenTK.Rewrite
{
foreach (var type in module.Types)
{
Rewrite(type);
Rewrite(type, options);
}
}
}
@ -136,7 +139,7 @@ namespace OpenTK.Rewrite
assembly.Write(file, write_params);
}
void Rewrite(TypeDefinition type)
void Rewrite(TypeDefinition type, IEnumerable<string> options)
{
var entry_points = type.Fields.FirstOrDefault(f => f.Name == "EntryPoints");
if (entry_points != null)
@ -146,7 +149,7 @@ namespace OpenTK.Rewrite
entry_signatures.AddRange(type.Methods
.Where(t => t.CustomAttributes.Any(a => a.AttributeType.Name == "SlotAttribute")));
Rewrite(type, entry_points, entry_signatures);
Rewrite(type, entry_points, entry_signatures, options);
RemoveNativeSignatures(type, entry_signatures);
}
@ -162,7 +165,7 @@ namespace OpenTK.Rewrite
}
void Rewrite(TypeDefinition type, FieldDefinition entry_points,
List<MethodDefinition> entry_signatures)
List<MethodDefinition> entry_signatures, IEnumerable<string> options)
{
// Rewrite all wrapper methods
var wrapper_signatures = new List<MethodDefinition>();
@ -182,7 +185,7 @@ namespace OpenTK.Rewrite
.First(a => a.AttributeType.Name == "SlotAttribute")
.ConstructorArguments[0].Value;
ProcessMethod(wrapper, signature, slot, entry_points);
ProcessMethod(wrapper, signature, slot, entry_points, options);
}
}
@ -192,7 +195,7 @@ namespace OpenTK.Rewrite
{
foreach (var nested_type in type.NestedTypes)
{
Rewrite(nested_type, entry_points, entry_signatures);
Rewrite(nested_type, entry_points, entry_signatures, options);
}
}
}
@ -223,7 +226,8 @@ namespace OpenTK.Rewrite
}
// Create body for method
static void ProcessMethod(MethodDefinition wrapper, MethodDefinition native, int slot, FieldDefinition entry_points)
static void ProcessMethod(MethodDefinition wrapper, MethodDefinition native, int slot,
FieldDefinition entry_points, IEnumerable<string> options)
{
var body = wrapper.Body;
var il = body.GetILProcessor();
@ -243,6 +247,12 @@ namespace OpenTK.Rewrite
int difference = native.Parameters.Count - wrapper.Parameters.Count;
EmitConvenienceWrapper(wrapper, native, difference, body, il);
}
DebugVariables vars = null;
if (options.Contains("-debug"))
{
vars = EmitDebugPrologue(wrapper, il);
}
// push the entry point address on the stack
EmitEntryPoint(entry_points, il, slot);
@ -266,6 +276,11 @@ namespace OpenTK.Rewrite
{
EmitStringEpilogue(wrapper, body, il);
}
if (options.Contains("-debug"))
{
EmitDebugEpilogue(wrapper, il, vars);
}
// return
il.Emit(OpCodes.Ret);
@ -279,6 +294,136 @@ namespace OpenTK.Rewrite
body.OptimizeMacros();
}
class DebugVariables
{
public TypeDefinition ErrorHelperType;
public VariableDefinition ErrorHelperLocal;
public MethodReference Get_CurrentContext;
public MethodReference Set_ErrorChecking;
public Instruction BeginTry;
}
static DebugVariables EmitDebugPrologue(MethodDefinition wrapper, ILProcessor il)
{
DebugVariables vars = null;
if (il.Body.Method.Name != "GetError")
{
// Pull out the namespace name, method fullname will look
// something like "type namespace.class::method(type arg)"
var module = il.Body.Method.FullName;
module = module.Substring(module.IndexOf(' ') + 1);
module = module.Substring(0, module.IndexOf("::"));
module = module.Substring(0, module.LastIndexOf('.'));
// Only works for Graphics modules due to hardcoded use of
// OpenTK.Graphics.GraphicsContext
if (module == "OpenTK.Graphics.OpenGL4" ||
module == "OpenTK.Graphics.OpenGL" ||
module == "OpenTK.Graphics.ES10" ||
module == "OpenTK.Graphics.ES11" ||
module == "OpenTK.Graphics.ES20" ||
module == "OpenTK.Graphics.ES30")
{
var errorHelperType = wrapper.Module.Types.FirstOrDefault(
type => type.FullName == string.Concat(module, "ErrorHelper"));
if (errorHelperType != null)
{
vars = new DebugVariables();
vars.ErrorHelperType = errorHelperType;
// Get the constructor that has no parameters
var ctor = vars.ErrorHelperType.GetConstructors().First(
c => !c.HasParameters);
var graphicsContext = wrapper.Module.Types.First(
type => type.FullName == "OpenTK.Graphics.GraphicsContext");
var iGraphicsContext = wrapper.Module.Types.First(
type => type.FullName == "OpenTK.Graphics.GraphicsContext");
vars.Get_CurrentContext = graphicsContext.Methods.First(
method => method.Name == "get_CurrentContext");
vars.Set_ErrorChecking = graphicsContext.Methods.First(
method => method.Name == "set_ErrorChecking");
vars.ErrorHelperLocal = new VariableDefinition(vars.ErrorHelperType);
il.Body.Variables.Add(vars.ErrorHelperLocal);
il.Emit(OpCodes.Call, vars.Get_CurrentContext);
il.Emit(OpCodes.Newobj, ctor);
il.Emit(OpCodes.Stloc, vars.ErrorHelperLocal);
vars.BeginTry = Instruction.Create(OpCodes.Nop);
il.Append(vars.BeginTry);
// Special case Begin to turn off error checking.
if (il.Body.Method.Name == "Begin")
{
il.Emit(OpCodes.Call, vars.Get_CurrentContext);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Conv_I1);
il.Emit(OpCodes.Call, vars.Set_ErrorChecking);
}
}
}
}
return vars;
}
static void EmitDebugEpilogue(MethodDefinition wrapper, ILProcessor il, DebugVariables vars)
{
if (vars != null)
{
var disposeMethod = vars.ErrorHelperType.Methods.First(
method => method.Name == "Dispose");
// Store then reload the result from the call
var resultLocal = new VariableDefinition(wrapper.ReturnType);
if (resultLocal.VariableType != Program.TypeVoid)
{
il.Body.Variables.Add(resultLocal);
il.Emit(OpCodes.Stloc, resultLocal);
}
// Special case End to turn on error checking.
if (il.Body.Method.Name == "End")
{
il.Emit(OpCodes.Call, vars.Get_CurrentContext);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Conv_I1);
il.Emit(OpCodes.Call, vars.Set_ErrorChecking);
}
// We need a NOP to set up the finally handler range correctly.
var nopInstruction = Instruction.Create(OpCodes.Nop);
var loadInstruction = Instruction.Create(OpCodes.Ldloca, vars.ErrorHelperLocal);
var disposeInstruction = Instruction.Create(OpCodes.Call, disposeMethod);
var leaveInstruction = Instruction.Create(OpCodes.Leave, nopInstruction);
il.Append(loadInstruction);
il.Append(disposeInstruction);
il.Append(leaveInstruction);
il.Append(nopInstruction);
var finallyHandler = new ExceptionHandler(ExceptionHandlerType.Finally);
finallyHandler.TryStart = vars.BeginTry;
finallyHandler.TryEnd = loadInstruction;
finallyHandler.HandlerStart = loadInstruction;
finallyHandler.HandlerEnd = nopInstruction;
il.Body.ExceptionHandlers.Add(finallyHandler);
if (resultLocal.VariableType != Program.TypeVoid)
{
il.Emit(OpCodes.Ldloc, resultLocal);
}
}
}
private static void EmitReturnTypeWrapper(MethodDefinition wrapper, MethodDefinition native, MethodBody body, ILProcessor il)
{
if (wrapper.Parameters.Count < native.Parameters.Count)

View file

@ -811,9 +811,9 @@
</PropertyGroup>
<ItemGroup />
<Target Name="AfterBuild">
<Exec Command="$(OutputPath)..\..\Tools\Debug\Rewrite.exe $(OutputPath)OpenTK.dll ..\..\OpenTK.snk" Condition="$(OS) == 'Windows_NT' and $(Configuration) == 'Debug'" />
<Exec Command="$(OutputPath)..\..\Tools\Debug\Rewrite.exe $(OutputPath)OpenTK.dll ..\..\OpenTK.snk -debug" Condition="$(OS) == 'Windows_NT' and $(Configuration) == 'Debug'" />
<Exec Command="$(OutputPath)..\..\Tools\Release\Rewrite.exe $(OutputPath)OpenTK.dll ..\..\OpenTK.snk" Condition="$(OS) == 'Windows_NT' and $(Configuration) != 'Debug'" />
<Exec Command="mono $(OutputPath)..\..\Tools\Debug\Rewrite.exe $(OutputPath)OpenTK.dll ..\..\OpenTK.snk" Condition="$(OS) != 'Windows_NT' and $(Configuration) == 'Debug'" />
<Exec Command="mono $(OutputPath)..\..\Tools\Debug\Rewrite.exe $(OutputPath)OpenTK.dll ..\..\OpenTK.snk -debug" Condition="$(OS) != 'Windows_NT' and $(Configuration) == 'Debug'" />
<Exec Command="mono $(OutputPath)..\..\Tools\Release\Rewrite.exe $(OutputPath)OpenTK.dll ..\..\OpenTK.snk" Condition="$(OS) != 'Windows_NT' and $(Configuration) != 'Debug'" />
</Target>
<ProjectExtensions>