[Rewrite] Refactored string prologue/epilogues
We currently have three categories of string parameters: `string`, `string[]` and `StringBuilder`. (OpenTK 1.2 adds one more: `ref string`.) Each category needs to be marshaled separately into a native character array. This commit implements the following changes: - string[] epilogues are now correctly emitted, instead of being ignored. - string[] prologues and epilogues now use the same local variable name. - all epilogues are now generated with a single pass over the function parameters, instead of requiring a separate pass for each category. - string prologues and epilogues now allocate local variables based on the relevant parameter *name* rather than the parameter *type*. Fixes issue #144.
This commit is contained in:
parent
c8a5bf5e32
commit
c32bf4ec5d
1 changed files with 127 additions and 112 deletions
|
@ -286,18 +286,8 @@ namespace OpenTK.Rewrite
|
|||
{
|
||||
EmitReturnTypeWrapper(wrapper, native, body, il);
|
||||
}
|
||||
if (wrapper.Parameters.Any(p => p.ParameterType.Name == "StringBuilder"))
|
||||
{
|
||||
EmitStringBuilderEpilogue(wrapper, native, body, il);
|
||||
}
|
||||
if (wrapper.Parameters.Any(p => p.ParameterType.Name == "String" && p.ParameterType.IsArray))
|
||||
{
|
||||
EmitStringArrayEpilogue(wrapper, body, il);
|
||||
}
|
||||
if (wrapper.Parameters.Any(p => p.ParameterType.Name == "String" && !p.ParameterType.IsArray))
|
||||
{
|
||||
EmitStringEpilogue(wrapper, body, il);
|
||||
}
|
||||
|
||||
EmitParameterEpilogues(wrapper, native, body, il);
|
||||
|
||||
if (options.Contains("-debug"))
|
||||
{
|
||||
|
@ -517,11 +507,63 @@ namespace OpenTK.Rewrite
|
|||
}
|
||||
}
|
||||
|
||||
static void EmitStringBuilderEpilogue(MethodDefinition wrapper, MethodDefinition native, MethodBody body, ILProcessor il)
|
||||
static void EmitParameterEpilogues(MethodDefinition wrapper, MethodDefinition native, MethodBody body, ILProcessor il)
|
||||
{
|
||||
for (int i = 0; i < wrapper.Parameters.Count; i++)
|
||||
foreach (var p in wrapper.Parameters)
|
||||
{
|
||||
var p = wrapper.Parameters[i].ParameterType;
|
||||
if (p.ParameterType.Name == "StringBuilder")
|
||||
{
|
||||
EmitStringBuilderEpilogue(wrapper, native, p, body, il);
|
||||
}
|
||||
|
||||
if (!p.ParameterType.IsArray && p.ParameterType.Name == "String")
|
||||
{
|
||||
EmitStringEpilogue(wrapper, p, body, il);
|
||||
}
|
||||
|
||||
if (p.ParameterType.IsArray && p.ParameterType.GetElementType().Name == "String")
|
||||
{
|
||||
EmitStringArrayEpilogue(wrapper, p, body, il);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void EmitStringBuilderParameter(MethodDefinition method, ParameterDefinition parameter, MethodBody body, ILProcessor il)
|
||||
{
|
||||
var p = parameter.ParameterType;
|
||||
|
||||
// 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 = parameter.Name + " _sb_ptr";
|
||||
body.Variables.Add(new VariableDefinition(variable_name, TypeIntPtr));
|
||||
int index = body.Variables.Count - 1;
|
||||
|
||||
// ptr = Marshal.AllocHGlobal(sb.Capacity + 1);
|
||||
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.
|
||||
}
|
||||
|
||||
static void EmitStringBuilderEpilogue(MethodDefinition wrapper, MethodDefinition native, ParameterDefinition parameter, MethodBody body, ILProcessor il)
|
||||
{
|
||||
var p = parameter.ParameterType;
|
||||
if (p.Name == "StringBuilder")
|
||||
{
|
||||
// void GetShaderInfoLog(..., StringBuilder foo)
|
||||
|
@ -541,10 +583,10 @@ namespace OpenTK.Rewrite
|
|||
var block = new ExceptionHandler(ExceptionHandlerType.Finally);
|
||||
block.TryStart = body.Instructions[0];
|
||||
|
||||
var variable_name = p.Name + " _sb_ptr";
|
||||
var variable_name = parameter.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.Ldarg, parameter.Index);
|
||||
il.Emit(OpCodes.Call, ptr_to_sb);
|
||||
|
||||
block.TryEnd = body.Instructions.Last();
|
||||
|
@ -556,10 +598,11 @@ namespace OpenTK.Rewrite
|
|||
block.HandlerEnd = body.Instructions.Last();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void EmitStringParameter(MethodDefinition wrapper, TypeReference p, MethodBody body, ILProcessor il)
|
||||
static void EmitStringParameter(MethodDefinition wrapper, ParameterDefinition parameter, MethodBody body, ILProcessor il)
|
||||
{
|
||||
var p = parameter.ParameterType;
|
||||
|
||||
// string marshaling:
|
||||
// IntPtr ptr = MarshalStringToPtr(str);
|
||||
// try { calli }
|
||||
|
@ -567,7 +610,7 @@ namespace OpenTK.Rewrite
|
|||
var marshal_str_to_ptr = wrapper.Module.Import(TypeBindingsBase.Methods.First(m => m.Name == "MarshalStringToPtr"));
|
||||
|
||||
// IntPtr ptr;
|
||||
var variable_name = p.Name + "_string_ptr";
|
||||
var variable_name = parameter.Name + "_string_ptr";
|
||||
body.Variables.Add(new VariableDefinition(variable_name, TypeIntPtr));
|
||||
int index = body.Variables.Count - 1;
|
||||
|
||||
|
@ -579,34 +622,30 @@ namespace OpenTK.Rewrite
|
|||
// The finally block will be emitted in the function epilogue
|
||||
}
|
||||
|
||||
static void EmitStringEpilogue(MethodDefinition wrapper, MethodBody body, ILProcessor il)
|
||||
{
|
||||
for (int i = 0; i < wrapper.Parameters.Count; i++)
|
||||
{
|
||||
var p = wrapper.Parameters[i].ParameterType;
|
||||
if (p.Name == "String" && !p.IsArray)
|
||||
static void EmitStringEpilogue(MethodDefinition wrapper, ParameterDefinition parameter, MethodBody body, ILProcessor il)
|
||||
{
|
||||
var p = parameter.ParameterType;
|
||||
var free = wrapper.Module.Import(TypeBindingsBase.Methods.First(m => m.Name == "FreeStringPtr"));
|
||||
|
||||
// FreeStringPtr(ptr)
|
||||
var variable_name = p.Name + "_string_ptr";
|
||||
var variable_name = parameter.Name + "_string_ptr";
|
||||
var v = body.Variables.First(m => m.Name == variable_name);
|
||||
il.Emit(OpCodes.Ldloc, v.Index);
|
||||
il.Emit(OpCodes.Call, free);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void EmitStringArrayParameter(MethodDefinition wrapper, TypeReference p, MethodBody body, ILProcessor il)
|
||||
static void EmitStringArrayParameter(MethodDefinition wrapper, ParameterDefinition parameter, MethodBody body, ILProcessor il)
|
||||
{
|
||||
var p = parameter.ParameterType;
|
||||
|
||||
// string[] masrhaling:
|
||||
// IntPtr ptr = MarshalStringArrayToPtr(strings);
|
||||
// try { calli }
|
||||
// finally { UnmarshalStringArray(ptr); }
|
||||
// finally { FreeStringArrayPtr(ptr); }
|
||||
var marshal_str_array_to_ptr = wrapper.Module.Import(TypeBindingsBase.Methods.First(m => m.Name == "MarshalStringArrayToPtr"));
|
||||
|
||||
// IntPtr ptr;
|
||||
var variable_name = p.Name + " _string_array_ptr";
|
||||
var variable_name = parameter.Name + "_string_array_ptr";
|
||||
body.Variables.Add(new VariableDefinition(variable_name, TypeIntPtr));
|
||||
int index = body.Variables.Count - 1;
|
||||
|
||||
|
@ -618,28 +657,30 @@ namespace OpenTK.Rewrite
|
|||
// The finally block will be emitted in the function epilogue
|
||||
}
|
||||
|
||||
static void EmitStringArrayEpilogue(MethodDefinition wrapper, MethodBody body, ILProcessor il)
|
||||
{
|
||||
for (int i = 0; i < wrapper.Parameters.Count; i++)
|
||||
{
|
||||
var p = wrapper.Parameters[i].ParameterType;
|
||||
if (p.Name == "String" && p.IsArray)
|
||||
static void EmitStringArrayEpilogue(MethodDefinition wrapper, ParameterDefinition parameter, MethodBody body, ILProcessor il)
|
||||
{
|
||||
// Note: only works for string vectors (1d arrays).
|
||||
// We do not (and will probably never) support 2d or higher string arrays
|
||||
var p = parameter.ParameterType;
|
||||
var free = wrapper.Module.Import(TypeBindingsBase.Methods.First(m => m.Name == "FreeStringArrayPtr"));
|
||||
var get_length = wrapper.Module.Import(TypeStringArray.Methods.First(m => m.Name == "get_Length"));
|
||||
|
||||
// FreeStringArrayPtr(string_array_ptr, string_array.Length)
|
||||
var variable_name = p.Name + "_string_array_ptr";
|
||||
var variable_name = parameter.Name + "_string_array_ptr";
|
||||
var v = body.Variables.First(m => m.Name == variable_name);
|
||||
|
||||
// load string_array_ptr
|
||||
il.Emit(OpCodes.Ldloc, v.Index);
|
||||
il.Emit(OpCodes.Ldarg, i);
|
||||
il.Emit(OpCodes.Callvirt, get_length);
|
||||
|
||||
// load string_array.Length
|
||||
il.Emit(OpCodes.Ldarg, parameter.Index);
|
||||
il.Emit(OpCodes.Ldlen);
|
||||
il.Emit(OpCodes.Conv_I4);
|
||||
|
||||
// call FreeStringArrayPtr
|
||||
il.Emit(OpCodes.Call, free);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitConvenienceWrapper(MethodDefinition wrapper,
|
||||
static void EmitConvenienceWrapper(MethodDefinition wrapper,
|
||||
MethodDefinition native, int difference, MethodBody body, ILProcessor il)
|
||||
{
|
||||
if (wrapper.Parameters.Count > 2)
|
||||
|
@ -707,43 +748,17 @@ namespace OpenTK.Rewrite
|
|||
int i;
|
||||
for (i = 0; i < method.Parameters.Count; i++)
|
||||
{
|
||||
var parameter = method.Parameters[i];
|
||||
var p = method.Module.Import(method.Parameters[i].ParameterType);
|
||||
il.Emit(OpCodes.Ldarg, i);
|
||||
|
||||
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, TypeIntPtr));
|
||||
int index = body.Variables.Count - 1;
|
||||
|
||||
// ptr = Marshal.AllocHGlobal(sb.Capacity + 1);
|
||||
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.
|
||||
EmitStringBuilderParameter(method, parameter, body, il);
|
||||
}
|
||||
else if (p.Name == "String" && !p.IsArray)
|
||||
{
|
||||
EmitStringParameter(method, p, body, il);
|
||||
EmitStringParameter(method, parameter, body, il);
|
||||
}
|
||||
else if (p.IsByReference)
|
||||
{
|
||||
|
@ -845,7 +860,7 @@ namespace OpenTK.Rewrite
|
|||
}
|
||||
else
|
||||
{
|
||||
EmitStringArrayParameter(method, p, body, il);
|
||||
EmitStringArrayParameter(method, parameter, body, il);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue