Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ParameterKeyProcessor.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 GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.IO;
6 using System.Linq;
7 using System.Runtime.CompilerServices;
8 using Mono.Cecil;
9 using Mono.Cecil.Cil;
10 using Mono.Cecil.Pdb;
11 using Mono.Cecil.Rocks;
13 
14 namespace SiliconStudio.AssemblyProcessor
15 {
17  {
18  public bool Process(AssemblyProcessorContext context)
19  {
20  var assembly = context.Assembly;
21  var fields = new List<FieldDefinition>();
22 
23  var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly);
24  if (mscorlibAssembly == null)
25  throw new InvalidOperationException("Missing mscorlib.dll from assembly");
26 
27  MethodDefinition parameterKeysMergeMethod = null;
28  TypeDefinition assemblyEffectKeysAttributeType = null;
29  var getTypeFromHandleMethod = new Lazy<MethodReference>(() =>
30  {
31  // Find Type.GetTypeFromHandle
32  var typeType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Type).FullName);
33  return assembly.MainModule.Import(typeType.Methods.First(x => x.Name == "GetTypeFromHandle"));
34  });
35 
36  var effectKeysStaticConstructors = new List<MethodReference>();
37 
38  foreach (var type in assembly.MainModule.GetTypes())
39  {
40  fields.Clear();
41 
42  foreach (var field in type.Fields.Where(x => x.IsStatic))
43  {
44  var fieldBaseType = field.FieldType;
45  while (fieldBaseType != null)
46  {
47  if (fieldBaseType.FullName == "SiliconStudio.Paradox.Effects.ParameterKey")
48  break;
49 
50  var resolvedFieldBaseType = fieldBaseType.Resolve();
51  if (resolvedFieldBaseType == null)
52  {
53  fieldBaseType = null;
54  break;
55  }
56 
57  fieldBaseType = resolvedFieldBaseType.BaseType;
58  }
59 
60  if (fieldBaseType == null)
61  continue;
62 
63  fields.Add(field);
64  }
65 
66  if (fields.Count == 0)
67  continue;
68 
69  // ParameterKey present means we should have a static cctor.
70  var cctor = type.GetStaticConstructor();
71  if (cctor == null)
72  continue;
73 
74  // Load necessary SiliconStudio.Paradox methods/attributes
75  if (parameterKeysMergeMethod == null)
76  {
77  AssemblyDefinition paradoxEngineAssembly;
78  try
79  {
80  paradoxEngineAssembly = assembly.Name.Name == "SiliconStudio.Paradox"
81  ? assembly
82  : context.AssemblyResolver.Resolve("SiliconStudio.Paradox");
83  }
84  catch (Exception)
85  {
86  Console.WriteLine("Error, cannot find [SiliconStudio.Paradox] assembly for processing ParameterKeyProcessor");
87  // We can't generate an exception, so we are just returning. It means that SiliconStudio.Paradox has not been generated so far.
88  return true;
89  }
90 
91  var parameterKeysType = paradoxEngineAssembly.MainModule.GetTypes().First(x => x.Name == "ParameterKeys");
92  parameterKeysMergeMethod = parameterKeysType.Methods.First(x => x.Name == "Merge");
93  assemblyEffectKeysAttributeType = paradoxEngineAssembly.MainModule.GetTypes().First(x => x.Name == "AssemblyEffectKeysAttribute");
94  }
95 
96  var cctorIL = cctor.Body.GetILProcessor();
97  var cctorInstructions = cctor.Body.Instructions;
98 
99  var keyClassName = type.Name;
100  if (keyClassName.EndsWith("Keys"))
101  keyClassName = keyClassName.Substring(0, keyClassName.Length - 4);
102 
103  keyClassName += '.';
104 
105  bool cctorModified = false;
106 
107  // Find field store instruction
108  for (int i = 0; i < cctorInstructions.Count; ++i)
109  {
110  var fieldInstruction = cctorInstructions[i];
111 
112  if (fieldInstruction.OpCode == OpCodes.Stsfld
113  && fields.Contains(fieldInstruction.Operand))
114  {
115  var activeField = (FieldReference)fieldInstruction.Operand;
116 
117  var nextInstruction = cctorInstructions[i + 1];
118  cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Ldsfld, activeField));
119  cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Ldtoken, type));
120  cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Call, getTypeFromHandleMethod.Value));
121  cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Ldstr, keyClassName + activeField.Name));
122  cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Call, assembly.MainModule.Import(parameterKeysMergeMethod)));
123  cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Castclass, activeField.FieldType));
124  cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Stsfld, activeField));
125  i = cctorInstructions.IndexOf(nextInstruction);
126  cctorModified = true;
127  }
128  }
129 
130  if (cctorModified)
131  {
132  effectKeysStaticConstructors.Add(cctor);
133  }
134  }
135 
136  if (effectKeysStaticConstructors.Count > 0)
137  {
138  // Add [AssemblyEffectKeysAttribute] to the assembly
139  assembly.CustomAttributes.Add(new CustomAttribute(assembly.MainModule.Import(assemblyEffectKeysAttributeType.GetConstructors().First(x => !x.HasParameters))));
140 
141  // Get or create module static constructor
142  var voidType = assembly.MainModule.Import(mscorlibAssembly.MainModule.GetTypeResolved(typeof(void).FullName));
143  var moduleClass = assembly.MainModule.Types.First(t => t.Name == "<Module>");
144  var staticConstructor = moduleClass.GetStaticConstructor();
145  if (staticConstructor == null)
146  {
147  staticConstructor = new MethodDefinition(".cctor",
148  MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
149  voidType);
150  staticConstructor.Body.GetILProcessor().Append(Instruction.Create(OpCodes.Ret));
151 
152  moduleClass.Methods.Add(staticConstructor);
153  }
154 
155  var il = staticConstructor.Body.GetILProcessor();
156 
157  var returnInstruction = staticConstructor.Body.Instructions.Last();
158  var newReturnInstruction = Instruction.Create(returnInstruction.OpCode);
159  newReturnInstruction.Operand = returnInstruction.Operand;
160 
161  returnInstruction.OpCode = OpCodes.Nop;
162  returnInstruction.Operand = null;
163 
164  var typeType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Type).FullName);
165  var typeHandleProperty = typeType.Properties.First(x => x.Name == "TypeHandle");
166  var getTypeHandleMethod = assembly.MainModule.Import(typeHandleProperty.GetMethod);
167 
168  var runtimeHelpersType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(RuntimeHelpers).FullName);
169  var runClassConstructorMethod = assembly.MainModule.Import(runtimeHelpersType.Methods.Single(x => x.IsPublic && x.Name == "RunClassConstructor" && x.Parameters.Count == 1 && x.Parameters[0].ParameterType.FullName == typeof(RuntimeTypeHandle).FullName));
170 
171  // Call every key class static constructor from the module static constructor so that they are properly constructed (because accessing through reflection might cause problems)
172  staticConstructor.Body.SimplifyMacros();
173  foreach (var effectKeysStaticConstructor in effectKeysStaticConstructors)
174  {
175  il.Append(Instruction.Create(OpCodes.Ldtoken, effectKeysStaticConstructor.DeclaringType));
176  il.Append(Instruction.Create(OpCodes.Call, getTypeFromHandleMethod.Value));
177  il.Append(Instruction.Create(OpCodes.Callvirt, getTypeHandleMethod));
178  il.Append(Instruction.Create(OpCodes.Call, runClassConstructorMethod));
179  }
180  il.Append(newReturnInstruction);
181  staticConstructor.Body.OptimizeMacros();
182  }
183 
184  return true;
185  }
186  }
187 }
Mono.Cecil.MethodAttributes MethodAttributes
Mono.Cecil.MethodAttributes MethodAttributes
The type of the serialized type will be passed as a generic arguments of the serializer. Example: serializer of A becomes instantiated as Serializer{A}.