25 using System.Collections.Generic;
28 using System.Reflection;
29 using System.Runtime.CompilerServices;
33 using Mono.Cecil.Rocks;
34 using SiliconStudio.Core;
38 namespace SiliconStudio.AssemblyProcessor
42 private AssemblyDefinition mscorlibAssembly;
43 private readonly List<TypeDefinition> classToRemoveList =
new List<TypeDefinition>();
44 private AssemblyDefinition assembly;
45 private TypeReference voidType;
46 private TypeReference voidPointerType;
47 private TypeReference intType;
51 this.assembly = context.Assembly;
52 mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly);
54 if (mscorlibAssembly == null)
56 LogError(
"Missing mscorlib.dll from assembly {0}", assembly.FullName);
57 throw new InvalidOperationException(
"Missing mscorlib.dll from assembly");
61 voidType = mscorlibAssembly.MainModule.GetTypeResolved(
"System.Void");
62 voidPointerType =
new PointerType(assembly.MainModule.Import(voidType));
63 intType = assembly.MainModule.Import(mscorlibAssembly.MainModule.GetTypeResolved(
"System.Int32"));
68 for (
int i = 0; i < assembly.CustomAttributes.Count; i++)
70 var customAttribute = assembly.CustomAttributes[i];
71 if (customAttribute.AttributeType.FullName == typeof(CompilationRelaxationsAttribute).FullName)
73 assembly.CustomAttributes.RemoveAt(i);
79 Log(
"Patch for assembly [{0}]", assembly.FullName);
80 foreach (var type
in assembly.MainModule.Types)
84 foreach (var type
in classToRemoveList)
85 assembly.MainModule.Types.Remove(type);
94 private void CreateModuleInit(MethodDefinition method)
97 | MethodAttributes.Assembly
98 | MethodAttributes.SpecialName
99 | MethodAttributes.RTSpecialName;
101 var moduleType = assembly.MainModule.GetTypeResolved(
"<Module>");
104 var cctor = moduleType.Methods.FirstOrDefault(moduleTypeMethod => moduleTypeMethod.Name ==
".cctor");
107 cctor =
new MethodDefinition(
".cctor", ModuleInitAttributes, method.ReturnType);
108 moduleType.Methods.Add(cctor);
111 bool isCallAlreadyDone = cctor.Body.Instructions.Any(instruction => instruction.OpCode == OpCodes.Call && instruction.Operand == method);
114 if (!isCallAlreadyDone)
116 var ilProcessor = cctor.Body.GetILProcessor();
117 var retInstruction = cctor.Body.Instructions.FirstOrDefault(instruction => instruction.OpCode == OpCodes.Ret);
118 var callMethod = ilProcessor.Create(OpCodes.Call, method);
120 if (retInstruction == null)
123 ilProcessor.Append(callMethod);
124 ilProcessor.Emit(OpCodes.Ret);
129 ilProcessor.InsertBefore(retInstruction, callMethod);
141 private void CreateWriteMethod(MethodDefinition method)
143 method.Body.Instructions.Clear();
144 method.Body.InitLocals =
true;
146 var gen = method.Body.GetILProcessor();
147 var paramT = method.GenericParameters[0];
150 method.Body.Variables.Add(
new VariableDefinition(intType));
152 method.Body.Variables.Add(
new VariableDefinition(
new PinnedType(
new ByReferenceType(paramT))));
155 gen.Emit(OpCodes.Ldarg_0);
158 gen.Emit(OpCodes.Ldarg_1);
159 gen.Emit(OpCodes.Stloc_1);
162 gen.Emit(OpCodes.Ldloc_1);
165 gen.Emit(OpCodes.Sizeof, paramT);
166 gen.Emit(OpCodes.Conv_I4);
167 gen.Emit(OpCodes.Stloc_0);
170 gen.Emit(OpCodes.Ldloc_0);
173 EmitCpblk(method, gen);
176 gen.Emit(OpCodes.Ldloc_0);
177 gen.Emit(OpCodes.Conv_I);
178 gen.Emit(OpCodes.Ldarg_0);
179 gen.Emit(OpCodes.Add);
182 gen.Emit(OpCodes.Ret);
185 private void ReplacePinStatement(MethodDefinition method, ILProcessor ilProcessor, Instruction fixedtoPatch)
187 var previousInstruction = fixedtoPatch.Previous;
189 if (previousInstruction.OpCode == OpCodes.Ldloc_0)
193 else if (previousInstruction.OpCode == OpCodes.Ldloc_1)
197 else if (previousInstruction.OpCode == OpCodes.Ldloc_2)
201 else if (previousInstruction.OpCode == OpCodes.Ldloc_3)
205 else if (previousInstruction.OpCode == OpCodes.Ldloc_S)
207 variableIndex = ((VariableReference)previousInstruction.Operand).Index;
209 else if (previousInstruction.OpCode == OpCodes.Ldloc)
211 variableIndex = ((VariableReference)previousInstruction.Operand).Index;
215 throw new InvalidOperationException(
"Could not find a load operation right before Interop.Pin");
218 var variable = ilProcessor.Body.Variables[variableIndex];
219 variable.VariableType = variable.VariableType.MakePinnedType();
221 ilProcessor.Remove(previousInstruction);
222 ilProcessor.Remove(fixedtoPatch);
225 private void ReplaceFixedStatement(MethodDefinition method, ILProcessor ilProcessor, Instruction fixedtoPatch)
227 var paramT = ((GenericInstanceMethod)fixedtoPatch.Operand).GenericArguments[0];
230 method.Body.Variables.Add(
new VariableDefinition(
"pin",
new PinnedType(
new ByReferenceType(paramT))));
232 int index = method.Body.Variables.Count - 1;
234 Instruction ldlocFixed;
235 Instruction stlocFixed;
239 stlocFixed = ilProcessor.Create(OpCodes.Stloc_0);
240 ldlocFixed = ilProcessor.Create(OpCodes.Ldloc_0);
243 stlocFixed = ilProcessor.Create(OpCodes.Stloc_1);
244 ldlocFixed = ilProcessor.Create(OpCodes.Ldloc_1);
247 stlocFixed = ilProcessor.Create(OpCodes.Stloc_2);
248 ldlocFixed = ilProcessor.Create(OpCodes.Ldloc_2);
251 stlocFixed = ilProcessor.Create(OpCodes.Stloc_3);
252 ldlocFixed = ilProcessor.Create(OpCodes.Ldloc_3);
255 stlocFixed = ilProcessor.Create(OpCodes.Stloc, index);
256 ldlocFixed = ilProcessor.Create(OpCodes.Ldloc, index);
259 ilProcessor.InsertBefore(fixedtoPatch, stlocFixed);
260 ilProcessor.Replace(fixedtoPatch, ldlocFixed);
263 private void ReplaceReadInline(MethodDefinition method, ILProcessor ilProcessor, Instruction fixedtoPatch)
265 var paramT = ((GenericInstanceMethod)fixedtoPatch.Operand).GenericArguments[0];
266 var copyInstruction = ilProcessor.Create(OpCodes.Ldobj, paramT);
267 ilProcessor.Replace(fixedtoPatch, copyInstruction);
270 private void ReplaceCopyInline(MethodDefinition method, ILProcessor ilProcessor, Instruction fixedtoPatch)
272 var paramT = ((GenericInstanceMethod)fixedtoPatch.Operand).GenericArguments[0];
273 var copyInstruction = ilProcessor.Create(OpCodes.Cpobj, paramT);
274 ilProcessor.Replace(fixedtoPatch, copyInstruction);
277 private void ReplaceSizeOfStructGeneric(MethodDefinition method, ILProcessor ilProcessor, Instruction fixedtoPatch)
279 var paramT = ((GenericInstanceMethod)fixedtoPatch.Operand).GenericArguments[0];
280 var copyInstruction = ilProcessor.Create(OpCodes.Sizeof, paramT);
281 ilProcessor.Replace(fixedtoPatch, copyInstruction);
284 private void ReplacePinStructGeneric(MethodDefinition method, ILProcessor ilProcessor, Instruction pinToPatch)
287 var nextStoreInstruction = pinToPatch.Next;
289 if (nextStoreInstruction.OpCode == OpCodes.Stloc_0)
293 else if (nextStoreInstruction.OpCode == OpCodes.Stloc_1)
297 else if (nextStoreInstruction.OpCode == OpCodes.Stloc_2)
301 else if (nextStoreInstruction.OpCode == OpCodes.Stloc_3)
305 else if (nextStoreInstruction.OpCode == OpCodes.Stloc_S)
307 variableIndex = ((VariableReference)nextStoreInstruction.Operand).Index;
309 else if (nextStoreInstruction.OpCode == OpCodes.Stloc)
311 variableIndex = ((VariableReference)nextStoreInstruction.Operand).Index;
315 throw new InvalidOperationException(
"Could not find a store operation right after Interop.Pin");
322 var variable = method.Body.Variables[variableIndex];
323 variable.VariableType = variable.VariableType
324 .MakeByReferenceType()
329 ilProcessor.Remove(pinToPatch);
332 for (
int index = 0; index < ilProcessor.Body.Instructions.Count; index++)
334 var instruction = ilProcessor.Body.Instructions[index];
335 if (instruction.OpCode == OpCodes.Ldloca && ((VariableReference)instruction.Operand).Index == variableIndex)
337 instruction.OpCode = OpCodes.Ldloc;
339 else if (instruction.OpCode == OpCodes.Ldloca_S && ((VariableReference)instruction.Operand).Index == variableIndex)
341 instruction.OpCode = OpCodes.Ldloc_S;
346 private void ReplaceIncrementPinnedStructGeneric(MethodDefinition method, ILProcessor ilProcessor, Instruction incrementPinnedToPatch)
348 var paramT = ((GenericInstanceMethod)incrementPinnedToPatch.Operand).GenericArguments[0];
350 var sizeOfInst = ilProcessor.Create(OpCodes.Sizeof, paramT);
352 ilProcessor.Replace(incrementPinnedToPatch, sizeOfInst);
353 ilProcessor.InsertAfter(sizeOfInst, ilProcessor.Create(OpCodes.Add));
363 private void CreateCastMethod(MethodDefinition method)
365 method.Body.Instructions.Clear();
366 method.Body.InitLocals =
true;
368 var gen = method.Body.GetILProcessor();
370 gen.Emit(OpCodes.Ldarg_0);
373 gen.Emit(OpCodes.Ret);
383 private void CreateCastArrayMethod(MethodDefinition method)
385 method.Body.Instructions.Clear();
386 method.Body.InitLocals =
true;
388 var gen = method.Body.GetILProcessor();
390 gen.Emit(OpCodes.Ldarg_0);
393 gen.Emit(OpCodes.Ret);
396 private void ReplaceFixedArrayStatement(MethodDefinition method, ILProcessor ilProcessor, Instruction fixedtoPatch)
398 var paramT = ((GenericInstanceMethod)fixedtoPatch.Operand).GenericArguments[0];
401 method.Body.Variables.Add(
new VariableDefinition(
"pin",
new PinnedType(
new ByReferenceType(paramT))));
403 int index = method.Body.Variables.Count - 1;
405 Instruction ldlocFixed;
406 Instruction stlocFixed;
410 stlocFixed = ilProcessor.Create(OpCodes.Stloc_0);
411 ldlocFixed = ilProcessor.Create(OpCodes.Ldloc_0);
414 stlocFixed = ilProcessor.Create(OpCodes.Stloc_1);
415 ldlocFixed = ilProcessor.Create(OpCodes.Ldloc_1);
418 stlocFixed = ilProcessor.Create(OpCodes.Stloc_2);
419 ldlocFixed = ilProcessor.Create(OpCodes.Ldloc_2);
422 stlocFixed = ilProcessor.Create(OpCodes.Stloc_3);
423 ldlocFixed = ilProcessor.Create(OpCodes.Ldloc_3);
426 stlocFixed = ilProcessor.Create(OpCodes.Stloc, index);
427 ldlocFixed = ilProcessor.Create(OpCodes.Ldloc, index);
431 var instructionLdci40 = ilProcessor.Create(OpCodes.Ldc_I4_0);
432 ilProcessor.InsertBefore(fixedtoPatch, instructionLdci40);
433 var instructionLdElema = ilProcessor.Create(OpCodes.Ldelema, paramT);
434 ilProcessor.InsertBefore(fixedtoPatch, instructionLdElema);
435 ilProcessor.InsertBefore(fixedtoPatch, stlocFixed);
436 ilProcessor.Replace(fixedtoPatch, ldlocFixed);
446 private void CreateWriteRangeMethod(MethodDefinition method)
448 method.Body.Instructions.Clear();
449 method.Body.InitLocals =
true;
451 var gen = method.Body.GetILProcessor();
452 var paramT = method.GenericParameters[0];
455 method.Body.Variables.Add(
new VariableDefinition(intType));
457 method.Body.Variables.Add(
new VariableDefinition(
new PinnedType(
new ByReferenceType(paramT))));
460 gen.Emit(OpCodes.Ldarg_0);
463 gen.Emit(OpCodes.Ldarg_1);
464 gen.Emit(OpCodes.Ldarg_2);
465 gen.Emit(OpCodes.Ldelema, paramT);
466 gen.Emit(OpCodes.Stloc_1);
469 gen.Emit(OpCodes.Ldloc_1);
472 gen.Emit(OpCodes.Sizeof, paramT);
473 gen.Emit(OpCodes.Conv_I4);
474 gen.Emit(OpCodes.Ldarg_3);
475 gen.Emit(OpCodes.Mul);
476 gen.Emit(OpCodes.Stloc_0);
479 gen.Emit(OpCodes.Ldloc_0);
482 EmitCpblk(method, gen);
485 gen.Emit(OpCodes.Ldloc_0);
486 gen.Emit(OpCodes.Conv_I);
487 gen.Emit(OpCodes.Ldarg_0);
488 gen.Emit(OpCodes.Add);
491 gen.Emit(OpCodes.Ret);
501 private void CreateReadMethod(MethodDefinition method)
503 method.Body.Instructions.Clear();
504 method.Body.InitLocals =
true;
506 var gen = method.Body.GetILProcessor();
507 var paramT = method.GenericParameters[0];
511 method.Body.Variables.Add(
new VariableDefinition(intType));
514 method.Body.Variables.Add(
new VariableDefinition(
new PinnedType(
new ByReferenceType(paramT))));
517 gen.Emit(OpCodes.Ldarg_1);
518 gen.Emit(OpCodes.Stloc_1);
521 gen.Emit(OpCodes.Ldloc_1);
524 gen.Emit(OpCodes.Ldarg_0);
527 gen.Emit(OpCodes.Sizeof, paramT);
528 gen.Emit(OpCodes.Conv_I4);
529 gen.Emit(OpCodes.Stloc_0);
532 gen.Emit(OpCodes.Ldloc_0);
535 EmitCpblk(method, gen);
538 gen.Emit(OpCodes.Ldloc_0);
539 gen.Emit(OpCodes.Conv_I);
540 gen.Emit(OpCodes.Ldarg_0);
541 gen.Emit(OpCodes.Add);
544 gen.Emit(OpCodes.Ret);
554 private void CreateReadRawMethod(MethodDefinition method)
556 method.Body.Instructions.Clear();
557 method.Body.InitLocals =
true;
559 var gen = method.Body.GetILProcessor();
560 var paramT = method.GenericParameters[0];
563 gen.Emit(OpCodes.Cpobj);
574 private void CreateReadRangeMethod(MethodDefinition method)
576 method.Body.Instructions.Clear();
577 method.Body.InitLocals =
true;
579 var gen = method.Body.GetILProcessor();
580 var paramT = method.GenericParameters[0];
583 method.Body.Variables.Add(
new VariableDefinition(intType));
585 method.Body.Variables.Add(
new VariableDefinition(
new PinnedType(
new ByReferenceType(paramT))));
588 gen.Emit(OpCodes.Ldarg_1);
589 gen.Emit(OpCodes.Ldarg_2);
590 gen.Emit(OpCodes.Ldelema, paramT);
591 gen.Emit(OpCodes.Stloc_1);
594 gen.Emit(OpCodes.Ldloc_1);
597 gen.Emit(OpCodes.Ldarg_0);
600 gen.Emit(OpCodes.Sizeof, paramT);
601 gen.Emit(OpCodes.Conv_I4);
602 gen.Emit(OpCodes.Ldarg_3);
603 gen.Emit(OpCodes.Conv_I4);
604 gen.Emit(OpCodes.Mul);
605 gen.Emit(OpCodes.Stloc_0);
608 gen.Emit(OpCodes.Ldloc_0);
611 EmitCpblk(method, gen);
614 gen.Emit(OpCodes.Ldloc_0);
615 gen.Emit(OpCodes.Conv_I);
616 gen.Emit(OpCodes.Ldarg_0);
617 gen.Emit(OpCodes.Add);
620 gen.Emit(OpCodes.Ret);
630 private void CreateMemcpy(MethodDefinition methodCopyStruct)
632 methodCopyStruct.Body.Instructions.Clear();
634 var gen = methodCopyStruct.Body.GetILProcessor();
636 gen.Emit(OpCodes.Ldarg_0);
637 gen.Emit(OpCodes.Ldarg_1);
638 gen.Emit(OpCodes.Ldarg_2);
639 gen.Emit(OpCodes.Unaligned, (byte)1);
640 gen.Emit(OpCodes.Cpblk);
643 gen.Emit(OpCodes.Ret);
653 private void CreateMemset(MethodDefinition methodSetStruct)
655 methodSetStruct.Body.Instructions.Clear();
657 var gen = methodSetStruct.Body.GetILProcessor();
659 gen.Emit(OpCodes.Ldarg_0);
660 gen.Emit(OpCodes.Ldarg_1);
661 gen.Emit(OpCodes.Ldarg_2);
662 gen.Emit(OpCodes.Unaligned, (byte)1);
663 gen.Emit(OpCodes.Initblk);
666 gen.Emit(OpCodes.Ret);
674 private void EmitCpblk(MethodDefinition method, ILProcessor gen)
676 var cpblk = gen.Create(OpCodes.Cpblk);
680 gen.Emit(OpCodes.Unaligned, (byte)1);
685 private List<string> GetSharpDXAttributes(MethodDefinition method)
687 var attributes =
new List<string>();
688 foreach (var customAttribute
in method.CustomAttributes)
690 if (customAttribute.AttributeType.FullName ==
"SharpDX.TagAttribute")
692 var value = customAttribute.ConstructorArguments[0].Value;
693 attributes.Add(value == null ? string.Empty : value.ToString());
704 bool PatchMethod(MethodDefinition method)
706 bool isSharpJit =
false;
708 var attributes = this.GetSharpDXAttributes(method);
709 if (attributes.Contains(
"SharpDX.ModuleInit"))
711 CreateModuleInit(method);
714 if (method.DeclaringType.Name ==
"Interop")
716 if (method.Name ==
"memcpy")
718 CreateMemcpy(method);
720 else if (method.Name ==
"memset")
722 CreateMemset(method);
724 else if ((method.Name ==
"Cast") || (method.Name ==
"CastOut"))
726 CreateCastMethod(method);
728 else if (method.Name ==
"CastArray")
730 CreateCastArrayMethod(method);
732 else if (method.Name ==
"Read" || (method.Name ==
"ReadOut") || (method.Name ==
"Read2D"))
734 if (method.Parameters.Count == 2)
735 CreateReadMethod(method);
737 CreateReadRangeMethod(method);
739 else if (method.Name ==
"Write" || (method.Name ==
"Write2D"))
741 if (method.Parameters.Count == 2)
742 CreateWriteMethod(method);
744 CreateWriteRangeMethod(method);
747 else if (method.HasBody)
749 var ilProcessor = method.Body.GetILProcessor();
751 var instructions = method.Body.Instructions;
752 Instruction instruction = null;
753 Instruction previousInstruction;
754 bool changes =
false;
755 for (
int i = 0; i < instructions.Count; i++)
757 previousInstruction = instruction;
758 instruction = instructions[i];
760 if (instruction.OpCode == OpCodes.Call && instruction.Operand is MethodReference)
762 var methodDescription = (MethodReference)instruction.Operand;
764 if (methodDescription is MethodDefinition)
766 foreach (var customAttribute
in ((MethodDefinition)methodDescription).CustomAttributes)
768 if (customAttribute.AttributeType.FullName == typeof(ObfuscationAttribute).FullName)
770 foreach (var arg
in customAttribute.Properties)
772 if (arg.Name ==
"Feature" && arg.Argument.Value != null)
774 var customValue = arg.Argument.Value.ToString();
775 if (customValue.StartsWith(
"SharpJit."))
783 if (isSharpJit)
break;
789 if (methodDescription.Name.StartsWith(
"Calli") && methodDescription.DeclaringType.Name ==
"LocalInterop")
791 var callSite =
new CallSite(methodDescription.ReturnType) { CallingConvention = MethodCallingConvention.StdCall };
794 for (
int j = 0; j < methodDescription.Parameters.Count - 1; j++)
796 var parameterDefinition = methodDescription.Parameters[j];
797 callSite.Parameters.Add(parameterDefinition);
801 var callIInstruction = ilProcessor.Create(OpCodes.Calli, callSite);
804 ilProcessor.Replace(instruction, callIInstruction);
806 else if (methodDescription.DeclaringType.Name ==
"Interop")
809 if (methodDescription.FullName.Contains(
"Fixed"))
811 if (methodDescription.Parameters[0].ParameterType.IsArray)
813 ReplaceFixedArrayStatement(method, ilProcessor, instruction);
817 ReplaceFixedStatement(method, ilProcessor, instruction);
820 else if (methodDescription.Name.StartsWith(
"ReadInline"))
822 this.ReplaceReadInline(method, ilProcessor, instruction);
824 else if (methodDescription.Name.StartsWith(
"CopyInline") || methodDescription.Name.StartsWith(
"WriteInline"))
826 this.ReplaceCopyInline(method, ilProcessor, instruction);
828 else if (methodDescription.Name.StartsWith(
"SizeOf"))
830 this.ReplaceSizeOfStructGeneric(method, ilProcessor, instruction);
832 else if (methodDescription.Name.StartsWith(
"Pin"))
834 if (methodDescription.Parameters[0].ParameterType.IsByReference)
836 this.ReplacePinStructGeneric(method, ilProcessor, instruction);
840 this.ReplacePinStatement(method, ilProcessor, instruction);
843 else if (methodDescription.Name.StartsWith(
"IncrementPinned"))
845 this.ReplaceIncrementPinnedStructGeneric(method, ilProcessor, instruction);
855 method.Body.OptimizeMacros();
861 bool containsSharpJit;
867 void PatchType(TypeDefinition type)
870 foreach (var method
in type.Methods)
871 if (PatchMethod(method))
872 containsSharpJit =
true;
875 if (!containsSharpJit && type.Name ==
"LocalInterop")
876 classToRemoveList.Add(type);
879 foreach (var typeDefinition
in type.NestedTypes)
880 PatchType(typeDefinition);
883 public void Log(
string message, params
object[] parameters)
885 Console.WriteLine(message, parameters);
888 public void LogError(
string message, params
object[] parameters)
890 Console.WriteLine(message, parameters);
895 Console.WriteLine(ex.ToString());
void LogError(Exception ex)
PlatformType
Describes the platform operating system.
bool Process(AssemblyProcessorContext context)
Mono.Cecil.MethodAttributes MethodAttributes
void Log(string message, params object[] parameters)
Mono.Cecil.MethodAttributes MethodAttributes
void LogError(string message, params object[] parameters)
Mono.Cecil.CallSite CallSite