From d5137d60575a7fac4f31602c818090c0d34a1048 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Mon, 2 Dec 2013 11:59:40 +0100 Subject: [PATCH] Initial implementation of manual StringBuilder marshaling (WIP) The implementation is based on Marshal.AllocHGlobal and Marshal.FreeHGlobal. This is not working correctly yet. --- Source/OpenTK.Rewrite/Program.cs | 118 ++++++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 11 deletions(-) diff --git a/Source/OpenTK.Rewrite/Program.cs b/Source/OpenTK.Rewrite/Program.cs index b46064ff..2f3b59ea 100644 --- a/Source/OpenTK.Rewrite/Program.cs +++ b/Source/OpenTK.Rewrite/Program.cs @@ -44,7 +44,15 @@ namespace OpenTK.Rewrite program.Rewrite(file, key); } + // mscorlib types static AssemblyDefinition mscorlib; + static TypeDefinition TypeMarshal; + static TypeDefinition TypeStringBuilder; + static TypeDefinition TypeVoid; + static TypeDefinition TypeIntPtr; + + // OpenTK.BindingsBase + static TypeDefinition TypeBindingsBase; void Rewrite(string file, string keyfile) { @@ -99,9 +107,15 @@ namespace OpenTK.Rewrite if (mscorlib == null) { - Console.Error.WriteLine("Falied to locate mscorlib"); + Console.Error.WriteLine("Failed to locate mscorlib"); return; } + TypeMarshal = mscorlib.MainModule.GetType("System.Runtime.InteropServices.Marshal"); + TypeStringBuilder = mscorlib.MainModule.GetType("System.Text.StringBuilder"); + TypeVoid = mscorlib.MainModule.GetType("System.Void"); + TypeIntPtr = mscorlib.MainModule.GetType("System.IntPtr"); + + TypeBindingsBase = assembly.Modules.Select(m => m.GetType("OpenTK.BindingsBase")).First(); foreach (var module in assembly.Modules) { @@ -238,6 +252,11 @@ namespace OpenTK.Rewrite EmitReturnTypeWrapper(wrapper, native, body, il); } + if (wrapper.Parameters.Any(p => p.ParameterType.Name == "StringBuilder")) + { + EmitStringBuilderEpilogue(wrapper, native, body, il); + } + // return il.Emit(OpCodes.Ret); @@ -266,25 +285,29 @@ namespace OpenTK.Rewrite var intptr_to_voidpointer = wrapper.Module.Import(mscorlib.MainModule.GetType("System.IntPtr").GetMethods() .First(m => - { - return + { + return m.Name == "op_Explicit" && - m.ReturnType.Name == "Void*"; - })); + m.ReturnType.Name == "Void*"; + })); var string_constructor = wrapper.Module.Import(mscorlib.MainModule.GetType("System.String").GetConstructors() .First(m => - { - var p = m.Parameters; - return p.Count > 0 && p[0].ParameterType.Name == "SByte*"; - })); + { + var p = m.Parameters; + return p.Count > 0 && p[0].ParameterType.Name == "SByte*"; + })); il.Emit(OpCodes.Call, intptr_to_voidpointer); il.Emit(OpCodes.Newobj, string_constructor); } + else if (wrapper.ReturnType.Resolve().IsEnum) + { + // Nothing to do + } else { - Console.Error.WriteLine("Return wrappers not implemented yet ({0})", native.Name); + Console.Error.WriteLine("Return wrapper for '{1}' not implemented yet ({0})", native.Name, wrapper.ReturnType.Name); } } else @@ -294,6 +317,47 @@ namespace OpenTK.Rewrite } } + static void EmitStringBuilderEpilogue(MethodDefinition wrapper, MethodDefinition native, MethodBody body, ILProcessor il) + { + for (int i = 0; i < wrapper.Parameters.Count; i++) + { + var p = wrapper.Parameters[i].ParameterType; + if (p.Name == "StringBuilder") + { + // void GetShaderInfoLog(..., StringBuilder foo) + // try { + // foo_sb_ptr = Marshal.AllocHGlobal(sb.Capacity + 1); -- already emitted + // glGetShaderInfoLog(..., foo_sb_ptr); -- already emitted + // MarshalStringBuilder(foo_sb_ptr, foo); + // } + // finally { + // Marshal.FreeHGlobal(foo_sb_ptr); + // } + + // Make sure we have imported BindingsBase::MasrhalPtrToStringBuilder and Marshal::FreeHGlobal + var ptr_to_sb = wrapper.Module.Import(TypeBindingsBase.Methods.First(m => m.Name == "MarshalPtrToStringBuilder")); + var free_hglobal = wrapper.Module.Import(TypeMarshal.Methods.First(m => m.Name == "FreeHGlobal")); + + var block = new ExceptionHandler(ExceptionHandlerType.Finally); + block.TryStart = body.Instructions[0]; + + var variable_name = p.Name + " _sb_ptr"; + var v = body.Variables.First(m => m.Name == variable_name); + il.Emit(OpCodes.Ldloc, v.Index); + il.Emit(OpCodes.Ldarg, i); + il.Emit(OpCodes.Call, ptr_to_sb); + + block.TryEnd = body.Instructions.Last(); + block.HandlerStart = body.Instructions.Last(); + + il.Emit(OpCodes.Ldloc, v.Index); + il.Emit(OpCodes.Call, free_hglobal); + + block.HandlerEnd = body.Instructions.Last(); + } + } + } + private static void EmitConvenienceWrapper(MethodDefinition wrapper, MethodDefinition native, int difference, MethodBody body, ILProcessor il) { @@ -348,7 +412,39 @@ namespace OpenTK.Rewrite var p = method.Module.Import(method.Parameters[i].ParameterType); il.Emit(OpCodes.Ldarg, i); - if (p.IsByReference) + if (p.Name == "StringBuilder") + { + // void GetShaderInfoLog(..., StringBuilder foo) + // IntPtr foo_sb_ptr; + // try { + // foo_sb_ptr = Marshal.AllocHGlobal(sb.Capacity + 1); + // glGetShaderInfoLog(..., foo_sb_ptr); + // MarshalPtrToStringBuilder(foo_sb_ptr, sb); + // } + // finally { + // Marshal.FreeHGlobal(sb_ptr); + // } + + // Make sure we have imported StringBuilder::Capacity and Marshal::AllocHGlobal + var sb_get_capacity = method.Module.Import(TypeStringBuilder.Methods.First(m => m.Name == "get_Capacity")); + var alloc_hglobal = method.Module.Import(TypeMarshal.Methods.First(m => m.Name == "AllocHGlobal")); + + // IntPtr ptr; + var variable_name = p.Name + " _sb_ptr"; + body.Variables.Add(new VariableDefinition(variable_name, nint)); + int index = body.Variables.Count - 1; + + // ptr = Marshal.AllocHGlobal(sb.Capacity + 1); + il.Emit(OpCodes.Ldarg, i); + il.Emit(OpCodes.Callvirt, sb_get_capacity); + il.Emit(OpCodes.Call, alloc_hglobal); + il.Emit(OpCodes.Stloc, index); + il.Emit(OpCodes.Ldloc, index); + + // We'll emit the try-finally block in the epilogue implementation, + // because we haven't yet emitted all necessary instructions here. + } + else if (p.IsByReference) { body.Variables.Add(new VariableDefinition(new PinnedType(p))); var index = body.Variables.Count - 1;