Rewrite support for [Slot] attribute
This commit is contained in:
parent
84a1e5a739
commit
40f992b5bd
8 changed files with 62867 additions and 236438 deletions
|
@ -1,4 +1,4 @@
|
|||
// OpenTK.Rewrite: IL rewriter for OpenTK.dll
|
||||
// OpenTK.Rewrite: IL rewriter for OpenTK.dll
|
||||
// Copyright (C) 2013 Stefanos Apostolopoulos
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
|
@ -100,104 +100,158 @@ namespace OpenTK.Rewrite
|
|||
|
||||
void Rewrite(TypeDefinition type)
|
||||
{
|
||||
foreach (var method in type.Methods)
|
||||
var entry_points = type.Fields.FirstOrDefault(f => f.Name == "EntryPoints");
|
||||
if (entry_points != null)
|
||||
{
|
||||
if (method.HasBody)
|
||||
foreach (var method in type.Methods)
|
||||
{
|
||||
ProcessMethodBody(method.Body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search the instruction stream for calls
|
||||
// to methods we need to rewrite.
|
||||
static void ProcessMethodBody(MethodBody body)
|
||||
{
|
||||
var instructions = body.Instructions;
|
||||
var il = body.GetILProcessor();
|
||||
|
||||
Instruction inst1 = instructions[0];
|
||||
Instruction inst2 = instructions[0];
|
||||
|
||||
for (int i = 1; i < instructions.Count; i++)
|
||||
{
|
||||
var inst = instructions[i];
|
||||
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
|
||||
// and not random methods that happen to have similar names.
|
||||
if (reference.DeclaringType.Name == "InteropHelper")
|
||||
if (method.CustomAttributes.Any(a => a.AttributeType.Name == "SlotAttribute"))
|
||||
{
|
||||
switch (reference.Name)
|
||||
{
|
||||
case "Call":
|
||||
case "CallReturn":
|
||||
RewriteCall(il, inst, reference);
|
||||
break;
|
||||
|
||||
case "Pin":
|
||||
RewritePin(il, inst, reference);
|
||||
break;
|
||||
}
|
||||
ProcessMethod(method, entry_points);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void RewriteCall(ILProcessor il, Instruction inst, MethodReference reference)
|
||||
// Create body for method
|
||||
static void ProcessMethod(MethodDefinition method, FieldDefinition entry_points)
|
||||
{
|
||||
var nint = method.DeclaringType.Module.Import(typeof(IntPtr));
|
||||
var body = method.Body;
|
||||
var instructions = body.Instructions;
|
||||
var il = body.GetILProcessor();
|
||||
var slot_attribute = method.CustomAttributes
|
||||
.First(a => a.AttributeType.Name == "SlotAttribute");
|
||||
var slot = (int)slot_attribute.ConstructorArguments.First().Value;
|
||||
|
||||
instructions.Clear();
|
||||
|
||||
// Declare pinned variables for every reference and array parameter
|
||||
// and push each parameter on the stack
|
||||
EmitParameters(method, nint, body, il);
|
||||
|
||||
// push the entry point address on the stack
|
||||
EmitEntryPoint(entry_points, il, slot);
|
||||
|
||||
// issue calli
|
||||
EmitCall(il, method);
|
||||
|
||||
// return
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
static void EmitParameters(MethodDefinition method, TypeReference nint, MethodBody body, ILProcessor il)
|
||||
{
|
||||
for (int i = 0; i < method.Parameters.Count; i++)
|
||||
{
|
||||
var p = method.Parameters[i];
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
break;
|
||||
case 1:
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
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;
|
||||
}
|
||||
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 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)
|
||||
{
|
||||
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,
|
||||
// we do not need any special preparation before emiting calli.
|
||||
var call = il.Create(OpCodes.Calli, signature);
|
||||
il.Replace(inst, call);
|
||||
il.Emit(OpCodes.Calli, signature);
|
||||
}
|
||||
|
||||
// IntPtr Pin<T>({ref} T{[];[,];[,,]} arg)
|
||||
|
|
|
@ -34,6 +34,7 @@ namespace OpenTK
|
|||
/// <summary>
|
||||
/// Indicates that this function is generated automatically by a tool.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class AutoGeneratedAttribute : Attribute
|
||||
{
|
||||
/// <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
|
@ -781,6 +781,7 @@
|
|||
<Compile Include="Graphics\ES20\ES20Enums.cs" />
|
||||
<Compile Include="Graphics\ES11\ES11.cs" />
|
||||
<Compile Include="Graphics\ES11\ES11Enums.cs" />
|
||||
<Compile Include="SlotAttribute.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
|
@ -791,7 +792,7 @@
|
|||
</PropertyGroup>
|
||||
<ItemGroup />
|
||||
<Target Name="AfterBuild">
|
||||
<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="$(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>
|
||||
</Project>
|
Loading…
Reference in a new issue