Initial implementation of manual StringBuilder marshaling (WIP)

The implementation is based on Marshal.AllocHGlobal and
Marshal.FreeHGlobal. This is not working correctly yet.
This commit is contained in:
Stefanos A 2013-12-02 11:59:40 +01:00
parent 45cdc2c1cd
commit d5137d6057

View file

@ -44,7 +44,15 @@ namespace OpenTK.Rewrite
program.Rewrite(file, key); program.Rewrite(file, key);
} }
// mscorlib types
static AssemblyDefinition mscorlib; 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) void Rewrite(string file, string keyfile)
{ {
@ -99,9 +107,15 @@ namespace OpenTK.Rewrite
if (mscorlib == null) if (mscorlib == null)
{ {
Console.Error.WriteLine("Falied to locate mscorlib"); Console.Error.WriteLine("Failed to locate mscorlib");
return; 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) foreach (var module in assembly.Modules)
{ {
@ -238,6 +252,11 @@ namespace OpenTK.Rewrite
EmitReturnTypeWrapper(wrapper, native, body, il); EmitReturnTypeWrapper(wrapper, native, body, il);
} }
if (wrapper.Parameters.Any(p => p.ParameterType.Name == "StringBuilder"))
{
EmitStringBuilderEpilogue(wrapper, native, body, il);
}
// return // return
il.Emit(OpCodes.Ret); il.Emit(OpCodes.Ret);
@ -266,25 +285,29 @@ namespace OpenTK.Rewrite
var intptr_to_voidpointer = wrapper.Module.Import(mscorlib.MainModule.GetType("System.IntPtr").GetMethods() var intptr_to_voidpointer = wrapper.Module.Import(mscorlib.MainModule.GetType("System.IntPtr").GetMethods()
.First(m => .First(m =>
{ {
return return
m.Name == "op_Explicit" && m.Name == "op_Explicit" &&
m.ReturnType.Name == "Void*"; m.ReturnType.Name == "Void*";
})); }));
var string_constructor = wrapper.Module.Import(mscorlib.MainModule.GetType("System.String").GetConstructors() var string_constructor = wrapper.Module.Import(mscorlib.MainModule.GetType("System.String").GetConstructors()
.First(m => .First(m =>
{ {
var p = m.Parameters; var p = m.Parameters;
return p.Count > 0 && p[0].ParameterType.Name == "SByte*"; return p.Count > 0 && p[0].ParameterType.Name == "SByte*";
})); }));
il.Emit(OpCodes.Call, intptr_to_voidpointer); il.Emit(OpCodes.Call, intptr_to_voidpointer);
il.Emit(OpCodes.Newobj, string_constructor); il.Emit(OpCodes.Newobj, string_constructor);
} }
else if (wrapper.ReturnType.Resolve().IsEnum)
{
// Nothing to do
}
else 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 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, private static void EmitConvenienceWrapper(MethodDefinition wrapper,
MethodDefinition native, int difference, MethodBody body, ILProcessor il) MethodDefinition native, int difference, MethodBody body, ILProcessor il)
{ {
@ -348,7 +412,39 @@ namespace OpenTK.Rewrite
var p = method.Module.Import(method.Parameters[i].ParameterType); var p = method.Module.Import(method.Parameters[i].ParameterType);
il.Emit(OpCodes.Ldarg, i); 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))); body.Variables.Add(new VariableDefinition(new PinnedType(p)));
var index = body.Variables.Count - 1; var index = body.Variables.Count - 1;