2 using System.Collections.Generic;
6 using Mono.Linker.Steps;
13 namespace MonoMac.Tuner {
15 public class MethodMapInjection : BaseStep {
17 struct ExportedMethod {
18 public readonly CustomAttribute attribute;
19 public readonly MethodDefinition method;
21 public ExportedMethod (CustomAttribute attribute, MethodDefinition method)
23 this.attribute = attribute;
28 ModuleDefinition module;
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;
42 protected override void ProcessAssembly (AssemblyDefinition assembly)
44 if (Annotations.GetAction (assembly) != AssemblyAction.Link)
47 module = assembly.MainModule;
49 foreach (TypeDefinition type in module.GetAllTypes ()) {
50 if (!type.IsNSObject ())
53 ProcessNSObject (type);
59 void PrepareImports ()
64 var corlib = Context.GetAssembly ("mscorlib");
66 void_type = Import (corlib, "System.Void");
68 var monomac = Context.GetAssembly ("MonoMac");
70 var dictionary = Import (corlib, "System.Collections.Generic.Dictionary`2");
72 var intptr = Import (corlib, "System.IntPtr");
73 var method_desc = Import (monomac, "MonoMac.ObjCRuntime.MethodDescription");
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;
80 dictionary_intptr_methoddesc_ctor = Import (".ctor", dic_i_md, false, void_type, Import (corlib, "System.Int32"));
82 dictionary_intptr_methoddesc_set_item = Import ("set_Item", dic_i_md, false, void_type,
83 dictionary.GenericParameters [0],
84 dictionary.GenericParameters [1]);
86 methoddesc_ctor = Import (".ctor", method_desc, false, void_type,
87 Import (corlib, "System.Reflection.MethodBase"),
88 Import (monomac, "MonoMac.ObjCRuntime.ArgumentSemantic"));
90 var selector = Import (monomac, "MonoMac.ObjCRuntime.Selector");
92 selector_get_handle = Import ("GetHandle", selector, true, intptr, Import (corlib, "System.String"));
93 selector_init = new FieldReference ("Init", selector, intptr);
95 var methodbase = Import (corlib, "System.Reflection.MethodBase");
97 methodbase_get_method_from_handle = Import ("GetMethodFromHandle", methodbase, true, methodbase, Import (corlib, "System.RuntimeMethodHandle"));
99 var type = Import (corlib, "System.Type");
101 type_get_type_from_handle = Import ("GetTypeFromHandle", type, true, type, Import (corlib, "System.RuntimeTypeHandle"));
103 var @class = Import (monomac, "MonoMac.ObjCRuntime.Class");
105 class_register_methods = Import ("RegisterMethods", @class, true, void_type, type, dic_i_md);
110 MethodReference Import (string name, TypeReference declaring_type, bool @static, TypeReference return_type, params TypeReference [] parameters_type)
112 var reference = new MethodReference (name, return_type, declaring_type) {
114 ExplicitThis = false,
115 CallingConvention = MethodCallingConvention.Default,
118 foreach (var parameter_type in parameters_type)
119 reference.Parameters.Add (new ParameterDefinition (parameter_type));
124 TypeReference Import (TypeReference type)
126 return module.Import (type);
129 TypeReference Import (AssemblyDefinition assembly, string type_name)
131 return Import (assembly.MainModule.GetType (type_name));
134 void ProcessNSObject (TypeDefinition type)
136 var exported = new List<ExportedMethod> ();
138 if (type.HasMethods) {
139 ProcessMethods (type, exported);
140 ProcessConstructors (type, exported);
143 if (exported.Count == 0)
146 InjectMethodMap (type, exported);
149 void InjectMethodMap (TypeDefinition type, List<ExportedMethod> exported_methods)
153 var cctor = GetTypeConstructor (type);
155 var selectors = MapSelectors (cctor);
157 var map = new VariableDefinition (dictionary_intptr_methoddesc);
158 map.Name = "$method_map";
159 cctor.Body.Variables.Add (map);
160 cctor.Body.SimplifyMacros ();
162 var il = cctor.Body.GetILProcessor ();
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),
170 foreach (var exported in exported_methods) {
172 instructions.Add (il.Create (OpCodes.Ldloc, map));
174 if (!IsDefaultConstructor (exported)) {
175 var selector_name = GetSelectorName (exported);
176 FieldReference selector;
178 if (selectors != null && selectors.TryGetValue (selector_name, out selector)) {
179 instructions.Add (il.Create (OpCodes.Ldsfld, selector));
181 instructions.AddRange (new [] {
182 il.Create (OpCodes.Ldstr, selector_name),
183 il.Create (OpCodes.Call, selector_get_handle),
187 instructions.Add (il.Create (OpCodes.Ldsfld, selector_init));
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),
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),
205 Append (il, instructions);
207 cctor.Body.OptimizeMacros ();
210 static Dictionary<string, FieldReference> MapSelectors (MethodDefinition cctor)
212 var instructions = cctor.Body.Instructions;
213 Dictionary<string, FieldReference> selectors = null;
215 for (int i = 0; i < instructions.Count; i++) {
216 var instruction = instructions [i];
218 FieldReference field;
219 if (!IsCreateSelector (instruction, out field))
222 if (selectors == null)
223 selectors = new Dictionary<string, FieldReference> ();
225 selectors.Add ((string) instruction.Operand, field);
231 static bool IsCreateSelector (Instruction instruction, out FieldReference field)
235 if (instruction.OpCode != OpCodes.Ldstr)
238 if (instruction.Next == null)
241 instruction = instruction.Next;
243 if (instruction.OpCode != OpCodes.Call)
246 var method = (MethodReference) instruction.Operand;
247 if (method.DeclaringType.Name != "Selector")
250 if (method.Name != "GetHandle" && method.Name != "sel_registerName")
253 if (instruction.Next == null)
256 instruction = instruction.Next;
258 if (instruction.OpCode != OpCodes.Stsfld)
261 field = instruction.Operand as FieldReference;
265 static bool IsDefaultConstructor (ExportedMethod exported)
267 return exported.attribute == null && exported.method.IsConstructor && !exported.method.IsStatic;
270 static void Append (ILProcessor il, IEnumerable<Instruction> instructions)
272 var method_instructions = il.Body.Instructions;
273 var last = method_instructions [method_instructions.Count - 1];
275 foreach (var instruction in instructions)
276 il.InsertBefore (last, instruction);
279 static int GetArgumentSemantic (ExportedMethod exported)
281 if (exported.attribute == null)
284 var arguments = exported.attribute.ConstructorArguments;
286 if (arguments.Count == 2)
287 return (int) arguments [1].Value;
289 if (arguments.Count == 1)
295 static string GetSelectorName (ExportedMethod exported)
297 var arguments = exported.attribute.ConstructorArguments;
299 if (arguments.Count == 0)
300 return exported.method.Name;
302 return (string) arguments [0].Value;
305 MethodDefinition GetTypeConstructor (TypeDefinition type)
307 return type.GetTypeConstructor () ?? CreateTypeConstructor (type);
310 MethodDefinition CreateTypeConstructor (TypeDefinition type)
312 var cctor = new MethodDefinition (".cctor", MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, void_type);
313 cctor.Body.GetILProcessor ().Emit (OpCodes.Ret);
315 type.Methods.Add (cctor);
320 void ProcessConstructors (TypeDefinition type, List<ExportedMethod> exported)
322 foreach (MethodDefinition ctor in type.GetConstructors ()) {
323 if (!ctor.HasParameters && !ctor.IsStatic) {
324 exported.Add (new ExportedMethod (null, ctor));
328 CustomAttribute export;
329 if (!TryGetExportAttribute (ctor, out export))
332 exported.Add (new ExportedMethod (export, ctor));
336 static bool TryGetExportAttribute (MethodDefinition method, out CustomAttribute export)
340 if (!method.HasCustomAttributes)
343 foreach (CustomAttribute attribute in method.CustomAttributes) {
344 if (attribute.AttributeType.FullName != "MonoMac.Foundation.ExportAttribute")
354 void ProcessMethods (TypeDefinition type, List<ExportedMethod> exported)
356 foreach (MethodDefinition method in type.GetMethods ()) {
357 CustomAttribute attribute;
358 if (TryGetExportAttribute (method, out attribute)) {
359 exported.Add (new ExportedMethod (attribute, method));
363 if (!method.IsVirtual)
366 var bases = Annotations.GetBaseMethods (method);
370 foreach (MethodDefinition @base in bases) {
371 if (@base.DeclaringType.IsInterface)
374 if (TryGetExportAttribute (@base, out attribute)) {
375 exported.Add (new ExportedMethod (attribute, method));