[w32handle] Stop returning 0 in every cases for locking/unlocking (#3926)
[mono.git] / mcs / tools / tuner / MonoMac.Tuner / MethodMapInjection.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4
5 using Mono.Linker;
6 using Mono.Linker.Steps;
7
8 using Mono.Tuner;
9
10 using Mono.Cecil;
11 using Mono.Cecil.Cil;
12
13 namespace MonoMac.Tuner {
14
15         public class MethodMapInjection : BaseStep {
16
17                 struct ExportedMethod {
18                         public readonly CustomAttribute attribute;
19                         public readonly MethodDefinition method;
20
21                         public ExportedMethod (CustomAttribute attribute, MethodDefinition method)
22                         {
23                                 this.attribute = attribute;
24                                 this.method = method;
25                         }
26                 }
27
28                 ModuleDefinition module;
29
30                 bool imported;
31                 TypeReference void_type;
32                 TypeReference dictionary_intptr_methoddesc;
33                 MethodReference dictionary_intptr_methoddesc_ctor;
34                 MethodReference dictionary_intptr_methoddesc_set_item;
35                 MethodReference methoddesc_ctor;
36                 MethodReference selector_get_handle;
37                 MethodReference methodbase_get_method_from_handle;
38                 MethodReference class_register_methods;
39                 MethodReference type_get_type_from_handle;
40                 FieldReference selector_init;
41
42                 protected override void ProcessAssembly (AssemblyDefinition assembly)
43                 {
44                         if (Annotations.GetAction (assembly) != AssemblyAction.Link)
45                                 return;
46
47                         module = assembly.MainModule;
48
49                         foreach (TypeDefinition type in module.GetAllTypes ()) {
50                                 if (!type.IsNSObject ())
51                                         continue;
52
53                                 ProcessNSObject (type);
54                         }
55
56                         imported = false;
57                 }
58
59                 void PrepareImports ()
60                 {
61                         if (imported)
62                                 return;
63
64                         var corlib = Context.GetAssembly ("mscorlib");
65
66                         void_type = Import (corlib, "System.Void");
67
68                         var monomac = Context.GetAssembly ("MonoMac");
69
70                         var dictionary = Import (corlib, "System.Collections.Generic.Dictionary`2");
71
72                         var intptr = Import (corlib, "System.IntPtr");
73                         var method_desc = Import (monomac, "MonoMac.ObjCRuntime.MethodDescription");
74
75                         var dic_i_md = new GenericInstanceType (dictionary);
76                         dic_i_md.GenericArguments.Add (intptr);
77                         dic_i_md.GenericArguments.Add (method_desc);
78                         dictionary_intptr_methoddesc = dic_i_md;
79
80                         dictionary_intptr_methoddesc_ctor = Import (".ctor", dic_i_md, false, void_type, Import (corlib, "System.Int32"));
81
82                         dictionary_intptr_methoddesc_set_item = Import ("set_Item", dic_i_md, false, void_type,
83                                 dictionary.GenericParameters [0],
84                                 dictionary.GenericParameters [1]);
85
86                         methoddesc_ctor = Import (".ctor", method_desc, false, void_type,
87                                 Import (corlib, "System.Reflection.MethodBase"),
88                                 Import (monomac, "MonoMac.ObjCRuntime.ArgumentSemantic"));
89
90                         var selector = Import (monomac, "MonoMac.ObjCRuntime.Selector");
91
92                         selector_get_handle = Import ("GetHandle", selector, true, intptr, Import (corlib, "System.String"));
93                         selector_init = new FieldReference ("Init", selector, intptr);
94
95                         var methodbase = Import (corlib, "System.Reflection.MethodBase");
96
97                         methodbase_get_method_from_handle = Import ("GetMethodFromHandle", methodbase, true, methodbase, Import (corlib, "System.RuntimeMethodHandle"));
98
99                         var type = Import (corlib, "System.Type");
100
101                         type_get_type_from_handle = Import ("GetTypeFromHandle", type, true, type, Import (corlib, "System.RuntimeTypeHandle"));
102
103                         var @class = Import (monomac, "MonoMac.ObjCRuntime.Class");
104
105                         class_register_methods = Import ("RegisterMethods", @class, true, void_type, type, dic_i_md);
106
107                         imported = true;
108                 }
109
110                 MethodReference Import (string name, TypeReference declaring_type, bool @static, TypeReference return_type, params TypeReference [] parameters_type)
111                 {
112                         var reference = new MethodReference (name, return_type, declaring_type) {
113                                 HasThis = !@static,
114                                 ExplicitThis = false,
115                                 CallingConvention = MethodCallingConvention.Default,
116                         };
117
118                         foreach (var parameter_type in parameters_type)
119                                 reference.Parameters.Add (new ParameterDefinition (parameter_type));
120
121                         return reference;
122                 }
123
124                 TypeReference Import (TypeReference type)
125                 {
126                         return module.Import (type);
127                 }
128
129                 TypeReference Import (AssemblyDefinition assembly, string type_name)
130                 {
131                         return Import (assembly.MainModule.GetType (type_name));
132                 }
133
134                 void ProcessNSObject (TypeDefinition type)
135                 {
136                         var exported = new List<ExportedMethod> ();
137
138                         if (type.HasMethods) {
139                                 ProcessMethods (type, exported);
140                                 ProcessConstructors (type, exported);
141                         }
142
143                         if (exported.Count == 0)
144                                 return;
145
146                         InjectMethodMap (type, exported);
147                 }
148
149                 void InjectMethodMap (TypeDefinition type, List<ExportedMethod> exported_methods)
150                 {
151                         PrepareImports ();
152
153                         var cctor = GetTypeConstructor (type);
154
155                         var selectors = MapSelectors (cctor);
156
157                         var map = new VariableDefinition (dictionary_intptr_methoddesc);
158                         map.Name = "$method_map";
159                         cctor.Body.Variables.Add (map);
160                         cctor.Body.SimplifyMacros ();
161
162                         var il = cctor.Body.GetILProcessor ();
163
164                         var instructions = new List<Instruction> {
165                                 il.Create (OpCodes.Ldc_I4, exported_methods.Count),
166                                 il.Create (OpCodes.Newobj, dictionary_intptr_methoddesc_ctor),
167                                 il.Create (OpCodes.Stloc, map),
168                         };
169
170                         foreach (var exported in exported_methods) {
171
172                                 instructions.Add (il.Create (OpCodes.Ldloc, map));
173
174                                 if (!IsDefaultConstructor (exported)) {
175                                         var selector_name = GetSelectorName (exported);
176                                         FieldReference selector;
177
178                                         if (selectors != null && selectors.TryGetValue (selector_name, out selector)) {
179                                                 instructions.Add (il.Create (OpCodes.Ldsfld, selector));
180                                         } else {
181                                                 instructions.AddRange (new [] {
182                                                         il.Create (OpCodes.Ldstr, selector_name),
183                                                         il.Create (OpCodes.Call, selector_get_handle),
184                                                 });
185                                         }
186                                 } else
187                                         instructions.Add (il.Create (OpCodes.Ldsfld, selector_init));
188
189                                 instructions.AddRange (new [] {
190                                         il.Create (OpCodes.Ldtoken, exported.method),
191                                         il.Create (OpCodes.Call, methodbase_get_method_from_handle),
192                                         il.Create (OpCodes.Ldc_I4, GetArgumentSemantic (exported)),
193                                         il.Create (OpCodes.Newobj, methoddesc_ctor),
194                                         il.Create (OpCodes.Callvirt, dictionary_intptr_methoddesc_set_item),
195                                 });
196                         }
197
198                         instructions.AddRange (new [] {
199                                 il.Create (OpCodes.Ldtoken, type),
200                                 il.Create (OpCodes.Call, type_get_type_from_handle),
201                                 il.Create (OpCodes.Ldloc, map),
202                                 il.Create (OpCodes.Call, class_register_methods),
203                         });
204
205                         Append (il, instructions);
206
207                         cctor.Body.OptimizeMacros ();
208                 }
209
210                 static Dictionary<string, FieldReference> MapSelectors (MethodDefinition cctor)
211                 {
212                         var instructions = cctor.Body.Instructions;
213                         Dictionary<string, FieldReference> selectors = null;
214
215                         for (int i = 0; i < instructions.Count; i++) {
216                                 var instruction = instructions [i];
217
218                                 FieldReference field;
219                                 if (!IsCreateSelector (instruction, out field))
220                                         continue;
221
222                                 if (selectors == null)
223                                         selectors = new Dictionary<string, FieldReference> ();
224
225                                 selectors.Add ((string) instruction.Operand, field);
226                         }
227
228                         return selectors;
229                 }
230
231                 static bool IsCreateSelector (Instruction instruction, out FieldReference field)
232                 {
233                         field = null;
234
235                         if (instruction.OpCode != OpCodes.Ldstr)
236                                 return false;
237
238                         if (instruction.Next == null)
239                                 return false;
240
241                         instruction = instruction.Next;
242
243                         if (instruction.OpCode != OpCodes.Call)
244                                 return false;
245
246                         var method = (MethodReference) instruction.Operand;
247                         if (method.DeclaringType.Name != "Selector")
248                                 return false;
249
250                         if (method.Name != "GetHandle" && method.Name != "sel_registerName")
251                                 return false;
252
253                         if (instruction.Next == null)
254                                 return false;
255
256                         instruction = instruction.Next;
257
258                         if (instruction.OpCode != OpCodes.Stsfld)
259                                 return false;
260
261                         field = instruction.Operand as FieldReference;
262                         return true;
263                 }
264
265                 static bool IsDefaultConstructor (ExportedMethod exported)
266                 {
267                         return exported.attribute == null && exported.method.IsConstructor && !exported.method.IsStatic;
268                 }
269
270                 static void Append (ILProcessor il, IEnumerable<Instruction> instructions)
271                 {
272                         var method_instructions = il.Body.Instructions;
273                         var last = method_instructions [method_instructions.Count - 1];
274
275                         foreach (var instruction in instructions)
276                                 il.InsertBefore (last, instruction);
277                 }
278
279                 static int GetArgumentSemantic (ExportedMethod exported)
280                 {
281                         if (exported.attribute == null)
282                                 return 0; // Assign
283
284                         var arguments = exported.attribute.ConstructorArguments;
285
286                         if (arguments.Count == 2)
287                                 return (int) arguments [1].Value;
288
289                         if (arguments.Count == 1)
290                                 return -1; // None
291
292                         return 0; // Assign
293                 }
294
295                 static string GetSelectorName (ExportedMethod exported)
296                 {
297                         var arguments = exported.attribute.ConstructorArguments;
298
299                         if (arguments.Count == 0)
300                                 return exported.method.Name;
301
302                         return (string) arguments [0].Value;
303                 }
304
305                 MethodDefinition GetTypeConstructor (TypeDefinition type)
306                 {
307                         return type.GetTypeConstructor () ?? CreateTypeConstructor (type);
308                 }
309
310                 MethodDefinition CreateTypeConstructor (TypeDefinition type)
311                 {
312                         var cctor = new MethodDefinition (".cctor", MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, void_type);
313                         cctor.Body.GetILProcessor ().Emit (OpCodes.Ret);
314
315                         type.Methods.Add (cctor);
316
317                         return cctor;
318                 }
319
320                 void ProcessConstructors (TypeDefinition type, List<ExportedMethod> exported)
321                 {
322                         foreach (MethodDefinition ctor in type.GetConstructors ()) {
323                                 if (!ctor.HasParameters && !ctor.IsStatic) {
324                                         exported.Add (new ExportedMethod (null, ctor));
325                                         continue;
326                                 }
327
328                                 CustomAttribute export;
329                                 if (!TryGetExportAttribute (ctor, out export))
330                                         continue;
331
332                                 exported.Add (new ExportedMethod (export, ctor));
333                         }
334                 }
335
336                 static bool TryGetExportAttribute (MethodDefinition method, out CustomAttribute export)
337                 {
338                         export = null;
339
340                         if (!method.HasCustomAttributes)
341                                 return false;
342
343                         foreach (CustomAttribute attribute in method.CustomAttributes) {
344                                 if (attribute.AttributeType.FullName != "MonoMac.Foundation.ExportAttribute")
345                                         continue;
346
347                                 export = attribute;
348                                 return true;
349                         }
350
351                         return false;
352                 }
353
354                 void ProcessMethods (TypeDefinition type, List<ExportedMethod> exported)
355                 {
356                         foreach (MethodDefinition method in type.GetMethods ()) {
357                                 CustomAttribute attribute;
358                                 if (TryGetExportAttribute (method, out attribute)) {
359                                         exported.Add (new ExportedMethod (attribute, method));
360                                         continue;
361                                 }
362
363                                 if (!method.IsVirtual)
364                                         continue;
365
366                                 var bases = Annotations.GetBaseMethods (method);
367                                 if (bases == null)
368                                         continue;
369
370                                 foreach (MethodDefinition @base in bases) {
371                                         if (@base.DeclaringType.IsInterface)
372                                                 continue;
373
374                                         if (TryGetExportAttribute (@base, out attribute)) {
375                                                 exported.Add (new ExportedMethod (attribute, method));
376                                                 break;
377                                         }
378                                 }
379                         }
380                 }
381         }
382 }