Initial work on Pin<> statements (WIP)

This commit is contained in:
Stefanos A. 2013-11-25 20:00:22 +01:00
parent bb15844deb
commit 84a1e5a739
3 changed files with 63 additions and 78 deletions

View file

@ -653,9 +653,7 @@ namespace Bind
Function f = new Function(func); Function f = new Function(func);
f.Body.Clear(); f.Body.Clear();
var handle_statements = new List<string>(); var pin_statements = new List<string>();
var handle_release_statements = new List<string>();
var fixed_statements = new List<string>();
var assign_statements = new List<string>(); var assign_statements = new List<string>();
var declaration_statements = new List<string>(); var declaration_statements = new List<string>();
@ -666,42 +664,31 @@ namespace Bind
index++; index++;
if (p.NeedsPin) if (p.NeedsPin)
{ {
if (p.WrapperType == WrapperTypes.GenericParameter) if (p.WrapperType == WrapperTypes.GenericParameter ||
{ p.WrapperType == WrapperTypes.PointerParameter ||
// Use GCHandle to obtain pointer to generic parameters and 'fixed' for arrays.
// This is because fixed can only take the address of fields, not managed objects.
handle_statements.Add(String.Format(
"{0} {1}_ptr = {0}.Alloc({1}, GCHandleType.Pinned);",
"GCHandle", p.Name));
handle_release_statements.Add(String.Format("{0}_ptr.Free();", p.Name));
// Due to the GCHandle-style pinning (which boxes value types), we need to assign the modified
// value back to the reference parameter (but only if it has an out or in/out flow direction).
if ((p.Flow == FlowDirection.Out || p.Flow == FlowDirection.Undefined) && p.Reference)
{
assign_statements.Add(String.Format(
"{0} = ({1}){0}_ptr.Target;",
p.Name, p.QualifiedType));
}
}
else if (p.WrapperType == WrapperTypes.PointerParameter ||
p.WrapperType == WrapperTypes.ArrayParameter || p.WrapperType == WrapperTypes.ArrayParameter ||
p.WrapperType == WrapperTypes.ReferenceParameter) p.WrapperType == WrapperTypes.ReferenceParameter)
{ {
// A fixed statement is issued for all non-generic pointers, arrays and references. if (f.Name.Contains("EdgeFlagPointerList"))
fixed_statements.Add(String.Format(
"fixed ({0}{3} {1} = {2})",
p.QualifiedType,
p.Name + "_ptr",
p.Array > 0 ? p.Name : "&" + p.Name,
pointer_levels[p.IndirectionLevel]));
// Arrays are not value types, so we don't need to do anything for them.
// Pointers are passed directly by value, so we don't need to assign them back either (they don't change).
if ((p.Flow == FlowDirection.Out || p.Flow == FlowDirection.Undefined) && p.Reference)
{ {
assign_statements.Add(String.Format("{0} = *{0}_ptr;", p.Name)); System.Diagnostics.Debugger.Break();
}
// Pin the parameter to obtain a pointer we can safely pass to unmanaged code
if (p.Pointer > 0)
{
declaration_statements.Add(String.Format("IntPtr {0}_ptr = new IntPtr({0});", p.Name));
}
pin_statements.Add(String.Format(
"{2}{0}_ptr = InteropHelper.Pin({1}{0});",
p.Name,
p.Reference ? "ref " : "",
p.Pointer == 0 ? "IntPtr " : ""));
// We also need to initialize out parameters, in order to make the compiler happy
if (p.Flow == FlowDirection.Out && p.Reference)
{
declaration_statements.Add(String.Format("{0} = default({1});", p.Name, p.QualifiedType));
} }
} }
else if (p.WrapperType == WrapperTypes.None) else if (p.WrapperType == WrapperTypes.None)
@ -769,8 +756,8 @@ namespace Bind
} }
} }
bool add_unsafe = !f.Unsafe && // Mark the body as unsafe if necessary
(fixed_statements.Count > 0 || declaration_statements.Count > 0); bool add_unsafe = !f.Unsafe && declaration_statements.Count > 0;
if (add_unsafe) if (add_unsafe)
{ {
f.Body.Add("unsafe"); f.Body.Add("unsafe");
@ -783,19 +770,9 @@ namespace Bind
f.Body.AddRange(declaration_statements); f.Body.AddRange(declaration_statements);
} }
if (fixed_statements.Count > 0) if (pin_statements.Count > 0)
{ {
f.Body.AddRange(fixed_statements); f.Body.AddRange(pin_statements);
f.Body.Add("{");
f.Body.Indent();
}
if (handle_statements.Count > 0)
{
f.Body.AddRange(handle_statements);
f.Body.Add("try");
f.Body.Add("{");
f.Body.Indent();
} }
// Hack: When creating untyped enum wrappers, it is possible that the wrapper uses an "All" // Hack: When creating untyped enum wrappers, it is possible that the wrapper uses an "All"
@ -901,33 +878,12 @@ namespace Bind
} }
} }
// Free all allocated GCHandles
if (handle_statements.Count > 0)
{
f.Body.Unindent();
f.Body.Add("}");
f.Body.Add("finally");
f.Body.Add("{");
f.Body.Indent();
f.Body.AddRange(handle_release_statements);
f.Body.Unindent();
f.Body.Add("}");
}
if (add_unsafe) if (add_unsafe)
{ {
f.Body.Unindent(); f.Body.Unindent();
f.Body.Add("}"); f.Body.Add("}");
} }
if (fixed_statements.Count > 0)
{
f.Body.Unindent();
f.Body.Add("}");
}
if ((Settings.Compatibility & Settings.Legacy.NoDebugHelpers) == 0) if ((Settings.Compatibility & Settings.Legacy.NoDebugHelpers) == 0)
{ {
if (f.TrimmedName != "GetError") if (f.TrimmedName != "GetError")
@ -1197,15 +1153,10 @@ namespace Bind
switch (p.WrapperType) switch (p.WrapperType)
{ {
case WrapperTypes.GenericParameter: case WrapperTypes.GenericParameter:
sb.Append(p.Name);
if (p.Generic) if (p.Generic)
{ {
sb.Append("(IntPtr)"); sb.Append("_ptr");
sb.Append(p.Name);
sb.Append("_ptr.AddrOfPinnedObject()");
}
else
{
sb.Append(p.Name);
} }
break; break;

View file

@ -137,6 +137,10 @@ namespace OpenTK.Rewrite
case "CallReturn": case "CallReturn":
RewriteCall(il, inst, reference); RewriteCall(il, inst, reference);
break; break;
case "Pin":
RewritePin(il, inst, reference);
break;
} }
} }
} }
@ -195,6 +199,16 @@ namespace OpenTK.Rewrite
var call = il.Create(OpCodes.Calli, signature); var call = il.Create(OpCodes.Calli, signature);
il.Replace(inst, call); il.Replace(inst, call);
} }
// IntPtr Pin<T>({ref} T{[];[,];[,,]} arg)
// Pin the parameter and return an unmanaged pointer
static void RewritePin(ILProcessor il, Instruction inst, MethodReference reference)
{
var greference = reference as GenericInstanceMethod;
if (greference == null)
throw new InvalidOperationException("reference must match generic method Pin<T>");
var ptype = greference.GenericArguments.First();
}
} }
} }

View file

@ -162,5 +162,25 @@ namespace OpenTK
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public static IntPtr Pin<T>(T[] arg)
{
throw new NotImplementedException();
}
public static IntPtr Pin<T>(T[,] arg)
{
throw new NotImplementedException();
}
public static IntPtr Pin<T>(T[,,] arg)
{
throw new NotImplementedException();
}
public static IntPtr Pin<T>(ref T arg)
{
throw new NotImplementedException();
}
} }
} }