From 10ca14ac6d31cc50f94d0a121b7218cf989941f6 Mon Sep 17 00:00:00 2001 From: Fraser Date: Thu, 20 Feb 2014 20:12:38 +0000 Subject: [PATCH 1/4] 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. --- Source/Generator.Rewrite/Program.cs | 167 ++++++++++++++++++++++++++-- Source/OpenTK/OpenTK.csproj | 4 +- 2 files changed, 158 insertions(+), 13 deletions(-) diff --git a/Source/Generator.Rewrite/Program.cs b/Source/Generator.Rewrite/Program.cs index b18ea7ff..b16c60b1 100644 --- a/Source/Generator.Rewrite/Program.cs +++ b/Source/Generator.Rewrite/Program.cs @@ -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 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 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 entry_signatures) + List entry_signatures, IEnumerable options) { // Rewrite all wrapper methods var wrapper_signatures = new List(); @@ -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 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) diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index 48c2d6ec..fc6f41d8 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -811,9 +811,9 @@ - + - + From 2f3e7a9493809c3eef172ec3b2eeb3ee2863d283 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 24 Feb 2014 18:22:04 +0100 Subject: [PATCH 2/4] [Rewrite] Fixed exit from try-finally block --- Source/Generator.Rewrite/Program.cs | 55 ++++++++++++++++++----------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/Source/Generator.Rewrite/Program.cs b/Source/Generator.Rewrite/Program.cs index b16c60b1..03ef19ee 100644 --- a/Source/Generator.Rewrite/Program.cs +++ b/Source/Generator.Rewrite/Program.cs @@ -237,6 +237,12 @@ namespace OpenTK.Rewrite // Declare pinned variables for every reference and array parameter // and push each parameter on the stack + DebugVariables vars = null; + if (options.Contains("-debug")) + { + vars = EmitDebugPrologue(wrapper, il); + } + // Patch convenience wrappers if (wrapper.Parameters.Count == native.Parameters.Count) { @@ -247,12 +253,6 @@ 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); @@ -325,24 +325,36 @@ namespace OpenTK.Rewrite module == "OpenTK.Graphics.ES20" || module == "OpenTK.Graphics.ES30") { - var errorHelperType = wrapper.Module.Types.FirstOrDefault( - type => type.FullName == string.Concat(module, "ErrorHelper")); + var errorHelperType = wrapper.Module.GetType(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); - + // GraphicsContext type var graphicsContext = wrapper.Module.Types.First( type => type.FullName == "OpenTK.Graphics.GraphicsContext"); + // IGraphicsContext type var iGraphicsContext = wrapper.Module.Types.First( - type => type.FullName == "OpenTK.Graphics.GraphicsContext"); + type => type.FullName == "OpenTK.Graphics.IGraphicsContext"); + // Get the constructor that takes a GraphicsContext parameter + var ctor = vars.ErrorHelperType.GetConstructors().FirstOrDefault( + c => c.Parameters.Count == 1 && + c.Parameters[0].ParameterType.FullName == iGraphicsContext.FullName); + + if (ctor == null) + { + throw new InvalidOperationException( + String.Format( + "{0} does needs a constructor taking {1}", + errorHelperType, + graphicsContext)); + } + + // GraphicsContext.CurrentContext property getter vars.Get_CurrentContext = graphicsContext.Methods.First( method => method.Name == "get_CurrentContext"); @@ -351,10 +363,11 @@ namespace OpenTK.Rewrite vars.ErrorHelperLocal = new VariableDefinition(vars.ErrorHelperType); + // var helper = new ErrorHelper(GraphicsContext.CurrentContext); il.Body.Variables.Add(vars.ErrorHelperLocal); + //il.Emit(OpCodes.Ldarga, vars.ErrorHelperLocal); // todo: fix this il.Emit(OpCodes.Call, vars.Get_CurrentContext); - il.Emit(OpCodes.Newobj, ctor); - il.Emit(OpCodes.Stloc, vars.ErrorHelperLocal); + il.Emit(OpCodes.Call, ctor); vars.BeginTry = Instruction.Create(OpCodes.Nop); il.Append(vars.BeginTry); @@ -378,12 +391,12 @@ namespace OpenTK.Rewrite { if (vars != null) { - var disposeMethod = vars.ErrorHelperType.Methods.First( + 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) + if (resultLocal.VariableType.FullName != Program.TypeVoid.FullName) { il.Body.Variables.Add(resultLocal); il.Emit(OpCodes.Stloc, resultLocal); @@ -402,11 +415,13 @@ namespace OpenTK.Rewrite 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); + var endFinallyInstruction = Instruction.Create(OpCodes.Endfinally); + var endTryInstruction = Instruction.Create(OpCodes.Leave, nopInstruction); + il.Append(endTryInstruction); il.Append(loadInstruction); il.Append(disposeInstruction); - il.Append(leaveInstruction); + il.Append(endFinallyInstruction); il.Append(nopInstruction); var finallyHandler = new ExceptionHandler(ExceptionHandlerType.Finally); @@ -417,7 +432,7 @@ namespace OpenTK.Rewrite il.Body.ExceptionHandlers.Add(finallyHandler); - if (resultLocal.VariableType != Program.TypeVoid) + if (resultLocal.VariableType.FullName != Program.TypeVoid.FullName) { il.Emit(OpCodes.Ldloc, resultLocal); } From 18ef63461066632e9916ad47ef2d4f7bb9fba7de Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 24 Feb 2014 22:33:12 +0100 Subject: [PATCH 3/4] [Build] Fixed ErrorHelper local variable We need to emit a ldloca instruction before constructing the ErrorHelper instance, in order to reference it in the finally block below. --- Source/Generator.Rewrite/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Generator.Rewrite/Program.cs b/Source/Generator.Rewrite/Program.cs index 03ef19ee..8374b850 100644 --- a/Source/Generator.Rewrite/Program.cs +++ b/Source/Generator.Rewrite/Program.cs @@ -363,9 +363,9 @@ namespace OpenTK.Rewrite vars.ErrorHelperLocal = new VariableDefinition(vars.ErrorHelperType); - // var helper = new ErrorHelper(GraphicsContext.CurrentContext); + // using (new ErrorHelper(GraphicsContext.CurrentContext)) { ... il.Body.Variables.Add(vars.ErrorHelperLocal); - //il.Emit(OpCodes.Ldarga, vars.ErrorHelperLocal); // todo: fix this + il.Emit(OpCodes.Ldloca, vars.ErrorHelperLocal); il.Emit(OpCodes.Call, vars.Get_CurrentContext); il.Emit(OpCodes.Call, ctor); From 346921981d773b5b931a7b0df53a34902d43bb34 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 24 Feb 2014 22:52:08 +0100 Subject: [PATCH 4/4] [Build] Added -debug to Rewrite commandline parameters --- Source/Generator.Rewrite/Generator.Rewrite.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Generator.Rewrite/Generator.Rewrite.csproj b/Source/Generator.Rewrite/Generator.Rewrite.csproj index 59c12385..bd14a051 100644 --- a/Source/Generator.Rewrite/Generator.Rewrite.csproj +++ b/Source/Generator.Rewrite/Generator.Rewrite.csproj @@ -23,7 +23,7 @@ DEBUG;TRACE prompt 4 - ../../OpenTK/Debug/OpenTK.dll ../../../OpenTK.snk + ../../OpenTK/Debug/OpenTK.dll ../../../OpenTK.snk -debug AnyCPU