Rewrite support for [Slot] attribute

This commit is contained in:
Stefanos A 2013-11-26 01:31:10 +01:00
parent 84a1e5a739
commit 40f992b5bd
8 changed files with 62867 additions and 236438 deletions

View file

@ -1,4 +1,4 @@
// OpenTK.Rewrite: IL rewriter for OpenTK.dll // OpenTK.Rewrite: IL rewriter for OpenTK.dll
// Copyright (C) 2013 Stefanos Apostolopoulos // Copyright (C) 2013 Stefanos Apostolopoulos
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
@ -99,105 +99,159 @@ namespace OpenTK.Rewrite
} }
void Rewrite(TypeDefinition type) void Rewrite(TypeDefinition type)
{
var entry_points = type.Fields.FirstOrDefault(f => f.Name == "EntryPoints");
if (entry_points != null)
{ {
foreach (var method in type.Methods) foreach (var method in type.Methods)
{ {
if (method.HasBody) if (method.CustomAttributes.Any(a => a.AttributeType.Name == "SlotAttribute"))
{ {
ProcessMethodBody(method.Body); ProcessMethod(method, entry_points);
}
} }
} }
} }
// Search the instruction stream for calls // Create body for method
// to methods we need to rewrite. static void ProcessMethod(MethodDefinition method, FieldDefinition entry_points)
static void ProcessMethodBody(MethodBody body)
{ {
var nint = method.DeclaringType.Module.Import(typeof(IntPtr));
var body = method.Body;
var instructions = body.Instructions; var instructions = body.Instructions;
var il = body.GetILProcessor(); var il = body.GetILProcessor();
var slot_attribute = method.CustomAttributes
.First(a => a.AttributeType.Name == "SlotAttribute");
var slot = (int)slot_attribute.ConstructorArguments.First().Value;
Instruction inst1 = instructions[0]; instructions.Clear();
Instruction inst2 = instructions[0];
for (int i = 1; i < instructions.Count; i++) // Declare pinned variables for every reference and array parameter
{ // and push each parameter on the stack
var inst = instructions[i]; EmitParameters(method, nint, body, il);
if ((inst.OpCode == OpCodes.Call || inst.OpCode == OpCodes.Callvirt) &&
inst.Operand is MethodReference)
{
var reference = inst.Operand as MethodReference;
// Make sure we are rewriting OpenTK.InteropHelper methods // push the entry point address on the stack
// and not random methods that happen to have similar names. EmitEntryPoint(entry_points, il, slot);
if (reference.DeclaringType.Name == "InteropHelper")
// issue calli
EmitCall(il, method);
// return
il.Emit(OpCodes.Ret);
}
static void EmitParameters(MethodDefinition method, TypeReference nint, MethodBody body, ILProcessor il)
{ {
switch (reference.Name) for (int i = 0; i < method.Parameters.Count; i++)
{ {
case "Call": var p = method.Parameters[i];
case "CallReturn": switch (i)
RewriteCall(il, inst, reference); {
case 0:
il.Emit(OpCodes.Ldarg_0);
break; break;
case 1:
case "Pin": il.Emit(OpCodes.Ldarg_1);
RewritePin(il, inst, reference); break;
case 2:
il.Emit(OpCodes.Ldarg_2);
break;
case 3:
il.Emit(OpCodes.Ldarg_3);
break;
default:
il.Emit(OpCodes.Ldarg_S, (byte)i);
break; break;
} }
if (p.ParameterType.IsArray || p.ParameterType.IsByReference)
{
body.Variables.Add(new VariableDefinition(new PinnedType(nint)));
var index = body.Variables.Count - 1;
switch (index)
{
case 0:
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldloc_0);
break;
case 1:
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Ldloc_1);
break;
case 2:
il.Emit(OpCodes.Stloc_2);
il.Emit(OpCodes.Ldloc_2);
break;
case 3:
il.Emit(OpCodes.Stloc_3);
il.Emit(OpCodes.Ldloc_3);
break;
default:
il.Emit(OpCodes.Stloc_S, (byte)index);
il.Emit(OpCodes.Ldloc_S, (byte)index);
break;
} }
//il.Emit(OpCodes.Conv_I);
} }
} }
} }
static void RewriteCall(ILProcessor il, Instruction inst, MethodReference reference) static void EmitEntryPoint(FieldDefinition entry_points, ILProcessor il, int slot)
{
il.Emit(OpCodes.Ldsfld, entry_points);
switch (slot)
{
case 0:
il.Emit(OpCodes.Ldc_I4_0);
break;
case 1:
il.Emit(OpCodes.Ldc_I4_1);
break;
case 2:
il.Emit(OpCodes.Ldc_I4_2);
break;
case 3:
il.Emit(OpCodes.Ldc_I4_3);
break;
case 4:
il.Emit(OpCodes.Ldc_I4_4);
break;
case 5:
il.Emit(OpCodes.Ldc_I4_5);
break;
case 6:
il.Emit(OpCodes.Ldc_I4_6);
break;
case 7:
il.Emit(OpCodes.Ldc_I4_7);
break;
case 8:
il.Emit(OpCodes.Ldc_I4_8);
break;
default:
if (slot < 128)
il.Emit(OpCodes.Ldc_I4_S, (byte)slot);
else
il.Emit(OpCodes.Ldc_I4, slot);
break;
}
il.Emit(OpCodes.Ldelem_I);
}
static void EmitCall(ILProcessor il, MethodReference reference)
{ {
var signature = new CallSite(reference.ReturnType) var signature = new CallSite(reference.ReturnType)
{ {
CallingConvention = MethodCallingConvention.Default, CallingConvention = MethodCallingConvention.Default,
}; };
if (reference is GenericInstanceMethod) foreach (var p in reference.Parameters)
{
var greference = reference as GenericInstanceMethod;
if (reference.Name.EndsWith("Return"))
{
// "TRet CallReturn<TRet, T0, ...>(T0 arg0, ..., IntPtr address)"
// The first generic parameter is the return type
// The rest are function parameters types
// The entry point address is not in the generic arg list
signature.ReturnType = greference.GenericArguments.First();
foreach (var ptype in greference.GenericArguments.Skip(1))
{
signature.Parameters.Add(new ParameterDefinition(ptype));
}
}
else
{
// "void Call<T0, ...>(T0 arg0, ..., IntPtr address)"
// The generic arguments define the function parameters
// The entry point address is not in the generic arg list
foreach (var ptype in greference.GenericArguments)
{
signature.Parameters.Add(new ParameterDefinition(ptype));
}
}
}
else
{
// Call(IntPtr address)
// The last parameter is the function address of this entry point.
// It is placed at the top of the stack (first parameter of calli)
// but is not actually part of the unmanaged signature, so we must
// not add it to the signature parameters.
foreach (var p in reference.Parameters.Take(reference.Parameters.Count - 1))
{ {
signature.Parameters.Add(p); signature.Parameters.Add(p);
} }
}
// Since the last parameter is always the entry point address, // Since the last parameter is always the entry point address,
// we do not need any special preparation before emiting calli. // we do not need any special preparation before emiting calli.
var call = il.Create(OpCodes.Calli, signature); il.Emit(OpCodes.Calli, signature);
il.Replace(inst, call);
} }
// IntPtr Pin<T>({ref} T{[];[,];[,,]} arg) // IntPtr Pin<T>({ref} T{[];[,];[,,]} arg)

View file

@ -34,6 +34,7 @@ namespace OpenTK
/// <summary> /// <summary>
/// Indicates that this function is generated automatically by a tool. /// Indicates that this function is generated automatically by a tool.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public sealed class AutoGeneratedAttribute : Attribute public sealed class AutoGeneratedAttribute : Attribute
{ {
/// <summary> /// <summary>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -781,6 +781,7 @@
<Compile Include="Graphics\ES20\ES20Enums.cs" /> <Compile Include="Graphics\ES20\ES20Enums.cs" />
<Compile Include="Graphics\ES11\ES11.cs" /> <Compile Include="Graphics\ES11\ES11.cs" />
<Compile Include="Graphics\ES11\ES11Enums.cs" /> <Compile Include="Graphics\ES11\ES11Enums.cs" />
<Compile Include="SlotAttribute.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
@ -791,7 +792,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup /> <ItemGroup />
<Target Name="AfterBuild"> <Target Name="AfterBuild">
<Exec Command="$(OutputPath)OpenTK.Rewrite.exe $(OutputPath)OpenTK.dll $(OutputPath)../../../OpenTK.snk" Condition="$(OS) == 'Windows_NT'" /> <!---<Exec Command="$(OutputPath)OpenTK.Rewrite.exe $(OutputPath)OpenTK.dll $(OutputPath)../../../OpenTK.snk" Condition="$(OS) == 'Windows_NT'" />
<Exec Command="mono $(OutputPath)OpenTK.Rewrite.exe $(OutputPath)OpenTK.dll $(OutputPath)../../../OpenTK.snk" Condition="$(OS) != 'Windows_NT'" /> <Exec Command="mono $(OutputPath)OpenTK.Rewrite.exe $(OutputPath)OpenTK.dll $(OutputPath)../../../OpenTK.snk" Condition="$(OS) != 'Windows_NT'" />-->
</Target> </Target>
</Project> </Project>