Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
InteropProcessor.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under MIT License. See LICENSE.md for details.
3 //
4 // Copyright (c) 2010-2012 SharpDX - Alexandre Mutel
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to deal
8 // in the Software without restriction, including without limitation the rights
9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 // copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 // THE SOFTWARE.
23 
24 using System;
25 using System.Collections.Generic;
26 using System.IO;
27 using System.Linq;
28 using System.Reflection;
29 using System.Runtime.CompilerServices;
30 
31 using Mono.Cecil;
32 using Mono.Cecil.Cil;
33 using Mono.Cecil.Rocks;
34 using SiliconStudio.Core;
37 
38 namespace SiliconStudio.AssemblyProcessor
39 {
41  {
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;
48 
49  public bool Process(AssemblyProcessorContext context)
50  {
51  this.assembly = context.Assembly;
52  mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly);
53 
54  if (mscorlibAssembly == null)
55  {
56  LogError("Missing mscorlib.dll from assembly {0}", assembly.FullName);
57  throw new InvalidOperationException("Missing mscorlib.dll from assembly");
58  }
59 
60  // Import void* and int32 from assembly using mscorlib specific version (2.0 or 4.0 depending on 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"));
64 
65  // Remove CompilationRelaxationsAttribute
66  if (context.Platform == PlatformType.WindowsStore || context.Platform == PlatformType.WindowsPhone)
67  {
68  for (int i = 0; i < assembly.CustomAttributes.Count; i++)
69  {
70  var customAttribute = assembly.CustomAttributes[i];
71  if (customAttribute.AttributeType.FullName == typeof(CompilationRelaxationsAttribute).FullName)
72  {
73  assembly.CustomAttributes.RemoveAt(i);
74  i--;
75  }
76  }
77  }
78 
79  Log("Patch for assembly [{0}]", assembly.FullName);
80  foreach (var type in assembly.MainModule.Types)
81  PatchType(type);
82 
83  // Remove All Interop classes
84  foreach (var type in classToRemoveList)
85  assembly.MainModule.Types.Remove(type);
86 
87  return true;
88  }
89 
90  /// <summary>
91  /// Creates a module init for a C# assembly.
92  /// </summary>
93  /// <param name="method">The method to add to the module init.</param>
94  private void CreateModuleInit(MethodDefinition method)
95  {
96  const MethodAttributes ModuleInitAttributes = MethodAttributes.Static
97  | MethodAttributes.Assembly
98  | MethodAttributes.SpecialName
99  | MethodAttributes.RTSpecialName;
100 
101  var moduleType = assembly.MainModule.GetTypeResolved("<Module>");
102 
103  // Get or create ModuleInit method
104  var cctor = moduleType.Methods.FirstOrDefault(moduleTypeMethod => moduleTypeMethod.Name == ".cctor");
105  if (cctor == null)
106  {
107  cctor = new MethodDefinition(".cctor", ModuleInitAttributes, method.ReturnType);
108  moduleType.Methods.Add(cctor);
109  }
110 
111  bool isCallAlreadyDone = cctor.Body.Instructions.Any(instruction => instruction.OpCode == OpCodes.Call && instruction.Operand == method);
112 
113  // If the method is not called, we can add it
114  if (!isCallAlreadyDone)
115  {
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);
119 
120  if (retInstruction == null)
121  {
122  // If a ret instruction is not present, add the method call and ret
123  ilProcessor.Append(callMethod);
124  ilProcessor.Emit(OpCodes.Ret);
125  }
126  else
127  {
128  // If a ret instruction is already present, just add the method to call before
129  ilProcessor.InsertBefore(retInstruction, callMethod);
130  }
131  }
132  }
133 
134  /// <summary>
135  /// Creates the write method with the following signature:
136  /// <code>
137  /// public static unsafe void* Write&lt;T&gt;(void* pDest, ref T data) where T : struct
138  /// </code>
139  /// </summary>
140  /// <param name="method">The method to patch</param>
141  private void CreateWriteMethod(MethodDefinition method)
142  {
143  method.Body.Instructions.Clear();
144  method.Body.InitLocals = true;
145 
146  var gen = method.Body.GetILProcessor();
147  var paramT = method.GenericParameters[0];
148  // Preparing locals
149  // local(0) int
150  method.Body.Variables.Add(new VariableDefinition(intType));
151  // local(1) T*
152  method.Body.Variables.Add(new VariableDefinition(new PinnedType(new ByReferenceType(paramT))));
153 
154  // Push (0) pDest for memcpy
155  gen.Emit(OpCodes.Ldarg_0);
156 
157  // fixed (void* pinnedData = &data[offset])
158  gen.Emit(OpCodes.Ldarg_1);
159  gen.Emit(OpCodes.Stloc_1);
160 
161  // Push (1) pinnedData for memcpy
162  gen.Emit(OpCodes.Ldloc_1);
163 
164  // totalSize = sizeof(T)
165  gen.Emit(OpCodes.Sizeof, paramT);
166  gen.Emit(OpCodes.Conv_I4);
167  gen.Emit(OpCodes.Stloc_0);
168 
169  // Push (2) totalSize
170  gen.Emit(OpCodes.Ldloc_0);
171 
172  // Emit cpblk
173  EmitCpblk(method, gen);
174 
175  // Return pDest + totalSize
176  gen.Emit(OpCodes.Ldloc_0);
177  gen.Emit(OpCodes.Conv_I);
178  gen.Emit(OpCodes.Ldarg_0);
179  gen.Emit(OpCodes.Add);
180 
181  // Ret
182  gen.Emit(OpCodes.Ret);
183  }
184 
185  private void ReplacePinStatement(MethodDefinition method, ILProcessor ilProcessor, Instruction fixedtoPatch)
186  {
187  var previousInstruction = fixedtoPatch.Previous;
188  int variableIndex;
189  if (previousInstruction.OpCode == OpCodes.Ldloc_0)
190  {
191  variableIndex = 0;
192  }
193  else if (previousInstruction.OpCode == OpCodes.Ldloc_1)
194  {
195  variableIndex = 1;
196  }
197  else if (previousInstruction.OpCode == OpCodes.Ldloc_2)
198  {
199  variableIndex = 2;
200  }
201  else if (previousInstruction.OpCode == OpCodes.Ldloc_3)
202  {
203  variableIndex = 3;
204  }
205  else if (previousInstruction.OpCode == OpCodes.Ldloc_S)
206  {
207  variableIndex = ((VariableReference)previousInstruction.Operand).Index;
208  }
209  else if (previousInstruction.OpCode == OpCodes.Ldloc)
210  {
211  variableIndex = ((VariableReference)previousInstruction.Operand).Index;
212  }
213  else
214  {
215  throw new InvalidOperationException("Could not find a load operation right before Interop.Pin");
216  }
217 
218  var variable = ilProcessor.Body.Variables[variableIndex];
219  variable.VariableType = variable.VariableType.MakePinnedType();
220 
221  ilProcessor.Remove(previousInstruction);
222  ilProcessor.Remove(fixedtoPatch);
223  }
224 
225  private void ReplaceFixedStatement(MethodDefinition method, ILProcessor ilProcessor, Instruction fixedtoPatch)
226  {
227  var paramT = ((GenericInstanceMethod)fixedtoPatch.Operand).GenericArguments[0];
228  // Preparing locals
229  // local(0) T& pinned
230  method.Body.Variables.Add(new VariableDefinition("pin", new PinnedType(new ByReferenceType(paramT))));
231 
232  int index = method.Body.Variables.Count - 1;
233 
234  Instruction ldlocFixed;
235  Instruction stlocFixed;
236  switch (index)
237  {
238  case 0:
239  stlocFixed = ilProcessor.Create(OpCodes.Stloc_0);
240  ldlocFixed = ilProcessor.Create(OpCodes.Ldloc_0);
241  break;
242  case 1:
243  stlocFixed = ilProcessor.Create(OpCodes.Stloc_1);
244  ldlocFixed = ilProcessor.Create(OpCodes.Ldloc_1);
245  break;
246  case 2:
247  stlocFixed = ilProcessor.Create(OpCodes.Stloc_2);
248  ldlocFixed = ilProcessor.Create(OpCodes.Ldloc_2);
249  break;
250  case 3:
251  stlocFixed = ilProcessor.Create(OpCodes.Stloc_3);
252  ldlocFixed = ilProcessor.Create(OpCodes.Ldloc_3);
253  break;
254  default:
255  stlocFixed = ilProcessor.Create(OpCodes.Stloc, index);
256  ldlocFixed = ilProcessor.Create(OpCodes.Ldloc, index);
257  break;
258  }
259  ilProcessor.InsertBefore(fixedtoPatch, stlocFixed);
260  ilProcessor.Replace(fixedtoPatch, ldlocFixed);
261  }
262 
263  private void ReplaceReadInline(MethodDefinition method, ILProcessor ilProcessor, Instruction fixedtoPatch)
264  {
265  var paramT = ((GenericInstanceMethod)fixedtoPatch.Operand).GenericArguments[0];
266  var copyInstruction = ilProcessor.Create(OpCodes.Ldobj, paramT);
267  ilProcessor.Replace(fixedtoPatch, copyInstruction);
268  }
269 
270  private void ReplaceCopyInline(MethodDefinition method, ILProcessor ilProcessor, Instruction fixedtoPatch)
271  {
272  var paramT = ((GenericInstanceMethod)fixedtoPatch.Operand).GenericArguments[0];
273  var copyInstruction = ilProcessor.Create(OpCodes.Cpobj, paramT);
274  ilProcessor.Replace(fixedtoPatch, copyInstruction);
275  }
276 
277  private void ReplaceSizeOfStructGeneric(MethodDefinition method, ILProcessor ilProcessor, Instruction fixedtoPatch)
278  {
279  var paramT = ((GenericInstanceMethod)fixedtoPatch.Operand).GenericArguments[0];
280  var copyInstruction = ilProcessor.Create(OpCodes.Sizeof, paramT);
281  ilProcessor.Replace(fixedtoPatch, copyInstruction);
282  }
283 
284  private void ReplacePinStructGeneric(MethodDefinition method, ILProcessor ilProcessor, Instruction pinToPatch)
285  {
286  // Next instruction should be a store to the variable that should be pinned
287  var nextStoreInstruction = pinToPatch.Next;
288  int variableIndex;
289  if (nextStoreInstruction.OpCode == OpCodes.Stloc_0)
290  {
291  variableIndex = 0;
292  }
293  else if (nextStoreInstruction.OpCode == OpCodes.Stloc_1)
294  {
295  variableIndex = 1;
296  }
297  else if (nextStoreInstruction.OpCode == OpCodes.Stloc_2)
298  {
299  variableIndex = 2;
300  }
301  else if (nextStoreInstruction.OpCode == OpCodes.Stloc_3)
302  {
303  variableIndex = 3;
304  }
305  else if (nextStoreInstruction.OpCode == OpCodes.Stloc_S)
306  {
307  variableIndex = ((VariableReference)nextStoreInstruction.Operand).Index;
308  }
309  else if (nextStoreInstruction.OpCode == OpCodes.Stloc)
310  {
311  variableIndex = ((VariableReference)nextStoreInstruction.Operand).Index;
312  }
313  else
314  {
315  throw new InvalidOperationException("Could not find a store operation right after Interop.Pin");
316  }
317 
318  // Transform variable from:
319  // valuetype Struct s
320  // to:
321  // valuetype Struct& modopt([mscorlib]System.Runtime.CompilerServices.IsExplicitlyDereferenced) pinned s,
322  var variable = method.Body.Variables[variableIndex];
323  variable.VariableType = variable.VariableType
324  .MakeByReferenceType()
325  //.MakeOptionalModifierType(typeof(IsExplicitlyDereferenced))
326  .MakePinnedType();
327 
328  // Remove call to Interop.Pin:
329  ilProcessor.Remove(pinToPatch);
330 
331  // Transform all ldloca with this variable into ldloc:
332  for (int index = 0; index < ilProcessor.Body.Instructions.Count; index++)
333  {
334  var instruction = ilProcessor.Body.Instructions[index];
335  if (instruction.OpCode == OpCodes.Ldloca && ((VariableReference)instruction.Operand).Index == variableIndex)
336  {
337  instruction.OpCode = OpCodes.Ldloc;
338  }
339  else if (instruction.OpCode == OpCodes.Ldloca_S && ((VariableReference)instruction.Operand).Index == variableIndex)
340  {
341  instruction.OpCode = OpCodes.Ldloc_S;
342  }
343  }
344  }
345 
346  private void ReplaceIncrementPinnedStructGeneric(MethodDefinition method, ILProcessor ilProcessor, Instruction incrementPinnedToPatch)
347  {
348  var paramT = ((GenericInstanceMethod)incrementPinnedToPatch.Operand).GenericArguments[0];
349 
350  var sizeOfInst = ilProcessor.Create(OpCodes.Sizeof, paramT);
351 
352  ilProcessor.Replace(incrementPinnedToPatch, sizeOfInst);
353  ilProcessor.InsertAfter(sizeOfInst, ilProcessor.Create(OpCodes.Add));
354  }
355 
356  /// <summary>
357  /// Creates the cast method with the following signature:
358  /// <code>
359  /// public static unsafe void* Cast&lt;T&gt;(ref T data) where T : struct
360  /// </code>
361  /// </summary>
362  /// <param name="method">The method cast.</param>
363  private void CreateCastMethod(MethodDefinition method)
364  {
365  method.Body.Instructions.Clear();
366  method.Body.InitLocals = true;
367 
368  var gen = method.Body.GetILProcessor();
369 
370  gen.Emit(OpCodes.Ldarg_0);
371 
372  // Ret
373  gen.Emit(OpCodes.Ret);
374  }
375 
376  /// <summary>
377  /// Creates the cast method with the following signature:
378  /// <code>
379  /// public static TCAST[] CastArray&lt;TCAST, T&gt;(T[] arrayData) where T : struct where TCAST : struct
380  /// </code>
381  /// </summary>
382  /// <param name="method">The method cast array.</param>
383  private void CreateCastArrayMethod(MethodDefinition method)
384  {
385  method.Body.Instructions.Clear();
386  method.Body.InitLocals = true;
387 
388  var gen = method.Body.GetILProcessor();
389 
390  gen.Emit(OpCodes.Ldarg_0);
391 
392  // Ret
393  gen.Emit(OpCodes.Ret);
394  }
395 
396  private void ReplaceFixedArrayStatement(MethodDefinition method, ILProcessor ilProcessor, Instruction fixedtoPatch)
397  {
398  var paramT = ((GenericInstanceMethod)fixedtoPatch.Operand).GenericArguments[0];
399  // Preparing locals
400  // local(0) T*
401  method.Body.Variables.Add(new VariableDefinition("pin", new PinnedType(new ByReferenceType(paramT))));
402 
403  int index = method.Body.Variables.Count - 1;
404 
405  Instruction ldlocFixed;
406  Instruction stlocFixed;
407  switch (index)
408  {
409  case 0:
410  stlocFixed = ilProcessor.Create(OpCodes.Stloc_0);
411  ldlocFixed = ilProcessor.Create(OpCodes.Ldloc_0);
412  break;
413  case 1:
414  stlocFixed = ilProcessor.Create(OpCodes.Stloc_1);
415  ldlocFixed = ilProcessor.Create(OpCodes.Ldloc_1);
416  break;
417  case 2:
418  stlocFixed = ilProcessor.Create(OpCodes.Stloc_2);
419  ldlocFixed = ilProcessor.Create(OpCodes.Ldloc_2);
420  break;
421  case 3:
422  stlocFixed = ilProcessor.Create(OpCodes.Stloc_3);
423  ldlocFixed = ilProcessor.Create(OpCodes.Ldloc_3);
424  break;
425  default:
426  stlocFixed = ilProcessor.Create(OpCodes.Stloc, index);
427  ldlocFixed = ilProcessor.Create(OpCodes.Ldloc, index);
428  break;
429  }
430 
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);
437  }
438 
439  /// <summary>
440  /// Creates the write range method with the following signature:
441  /// <code>
442  /// public static unsafe void* Write&lt;T&gt;(void* pDest, T[] data, int offset, int count) where T : struct
443  /// </code>
444  /// </summary>
445  /// <param name="method">The method copy struct.</param>
446  private void CreateWriteRangeMethod(MethodDefinition method)
447  {
448  method.Body.Instructions.Clear();
449  method.Body.InitLocals = true;
450 
451  var gen = method.Body.GetILProcessor();
452  var paramT = method.GenericParameters[0];
453  // Preparing locals
454  // local(0) int
455  method.Body.Variables.Add(new VariableDefinition(intType));
456  // local(1) T*
457  method.Body.Variables.Add(new VariableDefinition(new PinnedType(new ByReferenceType(paramT))));
458 
459  // Push (0) pDest for memcpy
460  gen.Emit(OpCodes.Ldarg_0);
461 
462  // fixed (void* pinnedData = &data[offset])
463  gen.Emit(OpCodes.Ldarg_1);
464  gen.Emit(OpCodes.Ldarg_2);
465  gen.Emit(OpCodes.Ldelema, paramT);
466  gen.Emit(OpCodes.Stloc_1);
467 
468  // Push (1) pinnedData for memcpy
469  gen.Emit(OpCodes.Ldloc_1);
470 
471  // totalSize = sizeof(T) * count
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);
477 
478  // Push (2) totalSize
479  gen.Emit(OpCodes.Ldloc_0);
480 
481  // Emit cpblk
482  EmitCpblk(method, gen);
483 
484  // Return pDest + totalSize
485  gen.Emit(OpCodes.Ldloc_0);
486  gen.Emit(OpCodes.Conv_I);
487  gen.Emit(OpCodes.Ldarg_0);
488  gen.Emit(OpCodes.Add);
489 
490  // Ret
491  gen.Emit(OpCodes.Ret);
492  }
493 
494  /// <summary>
495  /// Creates the read method with the following signature:
496  /// <code>
497  /// public static unsafe void* Read&lt;T&gt;(void* pSrc, ref T data) where T : struct
498  /// </code>
499  /// </summary>
500  /// <param name="method">The method copy struct.</param>
501  private void CreateReadMethod(MethodDefinition method)
502  {
503  method.Body.Instructions.Clear();
504  method.Body.InitLocals = true;
505 
506  var gen = method.Body.GetILProcessor();
507  var paramT = method.GenericParameters[0];
508 
509  // Preparing locals
510  // local(0) int
511  method.Body.Variables.Add(new VariableDefinition(intType));
512  // local(1) T*
513 
514  method.Body.Variables.Add(new VariableDefinition(new PinnedType(new ByReferenceType(paramT))));
515 
516  // fixed (void* pinnedData = &data[offset])
517  gen.Emit(OpCodes.Ldarg_1);
518  gen.Emit(OpCodes.Stloc_1);
519 
520  // Push (0) pinnedData for memcpy
521  gen.Emit(OpCodes.Ldloc_1);
522 
523  // Push (1) pSrc for memcpy
524  gen.Emit(OpCodes.Ldarg_0);
525 
526  // totalSize = sizeof(T)
527  gen.Emit(OpCodes.Sizeof, paramT);
528  gen.Emit(OpCodes.Conv_I4);
529  gen.Emit(OpCodes.Stloc_0);
530 
531  // Push (2) totalSize
532  gen.Emit(OpCodes.Ldloc_0);
533 
534  // Emit cpblk
535  EmitCpblk(method, gen);
536 
537  // Return pDest + totalSize
538  gen.Emit(OpCodes.Ldloc_0);
539  gen.Emit(OpCodes.Conv_I);
540  gen.Emit(OpCodes.Ldarg_0);
541  gen.Emit(OpCodes.Add);
542 
543  // Ret
544  gen.Emit(OpCodes.Ret);
545  }
546 
547  /// <summary>
548  /// Creates the read method with the following signature:
549  /// <code>
550  /// public static unsafe void Read&lt;T&gt;(void* pSrc, ref T data) where T : struct
551  /// </code>
552  /// </summary>
553  /// <param name="method">The method copy struct.</param>
554  private void CreateReadRawMethod(MethodDefinition method)
555  {
556  method.Body.Instructions.Clear();
557  method.Body.InitLocals = true;
558 
559  var gen = method.Body.GetILProcessor();
560  var paramT = method.GenericParameters[0];
561 
562  // Push (1) pSrc for memcpy
563  gen.Emit(OpCodes.Cpobj);
564 
565  }
566 
567  /// <summary>
568  /// Creates the read range method with the following signature:
569  /// <code>
570  /// public static unsafe void* Read&lt;T&gt;(void* pSrc, T[] data, int offset, int count) where T : struct
571  /// </code>
572  /// </summary>
573  /// <param name="method">The method copy struct.</param>
574  private void CreateReadRangeMethod(MethodDefinition method)
575  {
576  method.Body.Instructions.Clear();
577  method.Body.InitLocals = true;
578 
579  var gen = method.Body.GetILProcessor();
580  var paramT = method.GenericParameters[0];
581  // Preparing locals
582  // local(0) int
583  method.Body.Variables.Add(new VariableDefinition(intType));
584  // local(1) T*
585  method.Body.Variables.Add(new VariableDefinition(new PinnedType(new ByReferenceType(paramT))));
586 
587  // fixed (void* pinnedData = &data[offset])
588  gen.Emit(OpCodes.Ldarg_1);
589  gen.Emit(OpCodes.Ldarg_2);
590  gen.Emit(OpCodes.Ldelema, paramT);
591  gen.Emit(OpCodes.Stloc_1);
592 
593  // Push (0) pinnedData for memcpy
594  gen.Emit(OpCodes.Ldloc_1);
595 
596  // Push (1) pDest for memcpy
597  gen.Emit(OpCodes.Ldarg_0);
598 
599  // totalSize = sizeof(T) * count
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);
606 
607  // Push (2) totalSize
608  gen.Emit(OpCodes.Ldloc_0);
609 
610  // Emit cpblk
611  EmitCpblk(method, gen);
612 
613  // Return pDest + totalSize
614  gen.Emit(OpCodes.Ldloc_0);
615  gen.Emit(OpCodes.Conv_I);
616  gen.Emit(OpCodes.Ldarg_0);
617  gen.Emit(OpCodes.Add);
618 
619  // Ret
620  gen.Emit(OpCodes.Ret);
621  }
622 
623  /// <summary>
624  /// Creates the memcpy method with the following signature:
625  /// <code>
626  /// public static unsafe void memcpy(void* pDest, void* pSrc, int count)
627  /// </code>
628  /// </summary>
629  /// <param name="methodCopyStruct">The method copy struct.</param>
630  private void CreateMemcpy(MethodDefinition methodCopyStruct)
631  {
632  methodCopyStruct.Body.Instructions.Clear();
633 
634  var gen = methodCopyStruct.Body.GetILProcessor();
635 
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); // unaligned to 1
640  gen.Emit(OpCodes.Cpblk);
641 
642  // Ret
643  gen.Emit(OpCodes.Ret);
644  }
645 
646  /// <summary>
647  /// Creates the memset method with the following signature:
648  /// <code>
649  /// public static unsafe void memset(void* pDest, byte value, int count)
650  /// </code>
651  /// </summary>
652  /// <param name="methodSetStruct">The method set struct.</param>
653  private void CreateMemset(MethodDefinition methodSetStruct)
654  {
655  methodSetStruct.Body.Instructions.Clear();
656 
657  var gen = methodSetStruct.Body.GetILProcessor();
658 
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); // unaligned to 1
663  gen.Emit(OpCodes.Initblk);
664 
665  // Ret
666  gen.Emit(OpCodes.Ret);
667  }
668 
669  /// <summary>
670  /// Emits the cpblk method, supporting x86 and x64 platform.
671  /// </summary>
672  /// <param name="method">The method.</param>
673  /// <param name="gen">The gen.</param>
674  private void EmitCpblk(MethodDefinition method, ILProcessor gen)
675  {
676  var cpblk = gen.Create(OpCodes.Cpblk);
677  //gen.Emit(OpCodes.Sizeof, voidPointerType);
678  //gen.Emit(OpCodes.Ldc_I4_8);
679  //gen.Emit(OpCodes.Bne_Un_S, cpblk);
680  gen.Emit(OpCodes.Unaligned, (byte)1); // unaligned to 1
681  gen.Append(cpblk);
682 
683  }
684 
685  private List<string> GetSharpDXAttributes(MethodDefinition method)
686  {
687  var attributes = new List<string>();
688  foreach (var customAttribute in method.CustomAttributes)
689  {
690  if (customAttribute.AttributeType.FullName == "SharpDX.TagAttribute")
691  {
692  var value = customAttribute.ConstructorArguments[0].Value;
693  attributes.Add(value == null ? string.Empty : value.ToString());
694  }
695  }
696 
697  return attributes;
698  }
699 
700  /// <summary>
701  /// Patches the method.
702  /// </summary>
703  /// <param name="method">The method.</param>
704  bool PatchMethod(MethodDefinition method)
705  {
706  bool isSharpJit = false;
707 
708  var attributes = this.GetSharpDXAttributes(method);
709  if (attributes.Contains("SharpDX.ModuleInit"))
710  {
711  CreateModuleInit(method);
712  }
713 
714  if (method.DeclaringType.Name == "Interop")
715  {
716  if (method.Name == "memcpy")
717  {
718  CreateMemcpy(method);
719  }
720  else if (method.Name == "memset")
721  {
722  CreateMemset(method);
723  }
724  else if ((method.Name == "Cast") || (method.Name == "CastOut"))
725  {
726  CreateCastMethod(method);
727  }
728  else if (method.Name == "CastArray")
729  {
730  CreateCastArrayMethod(method);
731  }
732  else if (method.Name == "Read" || (method.Name == "ReadOut") || (method.Name == "Read2D"))
733  {
734  if (method.Parameters.Count == 2)
735  CreateReadMethod(method);
736  else
737  CreateReadRangeMethod(method);
738  }
739  else if (method.Name == "Write" || (method.Name == "Write2D"))
740  {
741  if (method.Parameters.Count == 2)
742  CreateWriteMethod(method);
743  else
744  CreateWriteRangeMethod(method);
745  }
746  }
747  else if (method.HasBody)
748  {
749  var ilProcessor = method.Body.GetILProcessor();
750 
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++)
756  {
757  previousInstruction = instruction;
758  instruction = instructions[i];
759 
760  if (instruction.OpCode == OpCodes.Call && instruction.Operand is MethodReference)
761  {
762  var methodDescription = (MethodReference)instruction.Operand;
763 
764  if (methodDescription is MethodDefinition)
765  {
766  foreach (var customAttribute in ((MethodDefinition)methodDescription).CustomAttributes)
767  {
768  if (customAttribute.AttributeType.FullName == typeof(ObfuscationAttribute).FullName)
769  {
770  foreach (var arg in customAttribute.Properties)
771  {
772  if (arg.Name == "Feature" && arg.Argument.Value != null)
773  {
774  var customValue = arg.Argument.Value.ToString();
775  if (customValue.StartsWith("SharpJit."))
776  {
777  isSharpJit = true;
778  break;
779  }
780  }
781  }
782  }
783  if (isSharpJit) break;
784  }
785  }
786 
787  if (!isSharpJit)
788  {
789  if (methodDescription.Name.StartsWith("Calli") && methodDescription.DeclaringType.Name == "LocalInterop")
790  {
791  var callSite = new CallSite(methodDescription.ReturnType) { CallingConvention = MethodCallingConvention.StdCall };
792  // Last parameter is the function ptr, so we don't add it as a parameter for calli
793  // as it is already an implicit parameter for calli
794  for (int j = 0; j < methodDescription.Parameters.Count - 1; j++)
795  {
796  var parameterDefinition = methodDescription.Parameters[j];
797  callSite.Parameters.Add(parameterDefinition);
798  }
799 
800  // Create calli Instruction
801  var callIInstruction = ilProcessor.Create(OpCodes.Calli, callSite);
802 
803  // Replace instruction
804  ilProcessor.Replace(instruction, callIInstruction);
805  }
806  else if (methodDescription.DeclaringType.Name == "Interop")
807  {
808  changes = true;
809  if (methodDescription.FullName.Contains("Fixed"))
810  {
811  if (methodDescription.Parameters[0].ParameterType.IsArray)
812  {
813  ReplaceFixedArrayStatement(method, ilProcessor, instruction);
814  }
815  else
816  {
817  ReplaceFixedStatement(method, ilProcessor, instruction);
818  }
819  }
820  else if (methodDescription.Name.StartsWith("ReadInline"))
821  {
822  this.ReplaceReadInline(method, ilProcessor, instruction);
823  }
824  else if (methodDescription.Name.StartsWith("CopyInline") || methodDescription.Name.StartsWith("WriteInline"))
825  {
826  this.ReplaceCopyInline(method, ilProcessor, instruction);
827  }
828  else if (methodDescription.Name.StartsWith("SizeOf"))
829  {
830  this.ReplaceSizeOfStructGeneric(method, ilProcessor, instruction);
831  }
832  else if (methodDescription.Name.StartsWith("Pin"))
833  {
834  if (methodDescription.Parameters[0].ParameterType.IsByReference)
835  {
836  this.ReplacePinStructGeneric(method, ilProcessor, instruction);
837  }
838  else
839  {
840  this.ReplacePinStatement(method, ilProcessor, instruction);
841  }
842  }
843  else if (methodDescription.Name.StartsWith("IncrementPinned"))
844  {
845  this.ReplaceIncrementPinnedStructGeneric(method, ilProcessor, instruction);
846  }
847  }
848  }
849  }
850  }
851 
852  if (changes)
853  {
854  // Compute offsets again (otherwise functions such as switch might fail).
855  method.Body.OptimizeMacros();
856  }
857  }
858  return isSharpJit;
859  }
860 
861  bool containsSharpJit;
862 
863  /// <summary>
864  /// Patches the type.
865  /// </summary>
866  /// <param name="type">The type.</param>
867  void PatchType(TypeDefinition type)
868  {
869  // Patch methods
870  foreach (var method in type.Methods)
871  if (PatchMethod(method))
872  containsSharpJit = true;
873 
874  // LocalInterop will be removed after the patch only for non SharpJit code
875  if (!containsSharpJit && type.Name == "LocalInterop")
876  classToRemoveList.Add(type);
877 
878  // Patch nested types
879  foreach (var typeDefinition in type.NestedTypes)
880  PatchType(typeDefinition);
881  }
882 
883  public void Log(string message, params object[] parameters)
884  {
885  Console.WriteLine(message, parameters);
886  }
887 
888  public void LogError(string message, params object[] parameters)
889  {
890  Console.WriteLine(message, parameters);
891  }
892 
893  public void LogError(Exception ex)
894  {
895  Console.WriteLine(ex.ToString());
896  }
897  }
898 }
PointerType
Type of a pointer device.
Definition: PointerType.cs:8
PlatformType
Describes the platform operating system.
Definition: PlatformType.cs:9
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