Add ErrorHelper to bindings.
authorFraser <frassle@gmail.com>
Thu, 20 Feb 2014 20:12:38 +0000 (20:12 +0000)
committerthefiddler <stapostol@gmail.com>
Tue, 25 Feb 2014 00:08:53 +0000 (01:08 +0100)
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
Source/OpenTK/OpenTK.csproj

index b18ea7f..b16c60b 100644 (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)
index 48c2d6e..fc6f41d 100644 (file)
   </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>