Merge remote branch 'upstream/master'
[mono.git] / mcs / class / IKVM.Reflection / Reader / Method.cs
1 /*
2   Copyright (C) 2009 Jeroen Frijters
3
4   This software is provided 'as-is', without any express or implied
5   warranty.  In no event will the authors be held liable for any damages
6   arising from the use of this software.
7
8   Permission is granted to anyone to use this software for any purpose,
9   including commercial applications, and to alter it and redistribute it
10   freely, subject to the following restrictions:
11
12   1. The origin of this software must not be misrepresented; you must not
13      claim that you wrote the original software. If you use this software
14      in a product, an acknowledgment in the product documentation would be
15      appreciated but is not required.
16   2. Altered source versions must be plainly marked as such, and must not be
17      misrepresented as being the original software.
18   3. This notice may not be removed or altered from any source distribution.
19
20   Jeroen Frijters
21   jeroen@frijters.net
22   
23 */
24 using System;
25 using System.Collections.Generic;
26 using System.Text;
27 using IKVM.Reflection.Metadata;
28
29 namespace IKVM.Reflection.Reader
30 {
31         sealed class MethodDefImpl : MethodInfo
32         {
33                 private readonly ModuleReader module;
34                 private readonly int index;
35                 private readonly TypeDefImpl declaringType;
36                 private MethodSignature lazyMethodSignature;
37                 private ParameterInfo returnParameter;
38                 private ParameterInfo[] parameters;
39                 private Type[] typeArgs;
40
41                 internal MethodDefImpl(ModuleReader module, TypeDefImpl declaringType, int index)
42                 {
43                         this.module = module;
44                         this.index = index;
45                         this.declaringType = declaringType;
46                 }
47
48                 public override MethodBody GetMethodBody()
49                 {
50                         return GetMethodBody(this);
51                 }
52
53                 internal MethodBody GetMethodBody(IGenericContext context)
54                 {
55                         if ((GetMethodImplementationFlags() & MethodImplAttributes.CodeTypeMask) != MethodImplAttributes.IL)
56                         {
57                                 // method is not IL
58                                 return null;
59                         }
60                         int rva = module.MethodDef.records[index].RVA;
61                         return rva == 0 ? null : new MethodBody(module, rva, context);
62                 }
63
64                 public override CallingConventions CallingConvention
65                 {
66                         get { return this.MethodSignature.CallingConvention; }
67                 }
68
69                 public override MethodAttributes Attributes
70                 {
71                         get { return (MethodAttributes)module.MethodDef.records[index].Flags; }
72                 }
73
74                 public override MethodImplAttributes GetMethodImplementationFlags()
75                 {
76                         return (MethodImplAttributes)module.MethodDef.records[index].ImplFlags;
77                 }
78
79                 public override ParameterInfo[] GetParameters()
80                 {
81                         PopulateParameters();
82                         return (ParameterInfo[])parameters.Clone();
83                 }
84
85                 private void PopulateParameters()
86                 {
87                         if (parameters == null)
88                         {
89                                 MethodSignature methodSignature = this.MethodSignature;
90                                 parameters = new ParameterInfo[methodSignature.GetParameterCount()];
91                                 int parameter = module.MethodDef.records[index].ParamList - 1;
92                                 int end = module.MethodDef.records.Length > index + 1 ? module.MethodDef.records[index + 1].ParamList - 1 : module.Param.records.Length;
93                                 for (; parameter < end; parameter++)
94                                 {
95                                         int seq = module.Param.records[parameter].Sequence - 1;
96                                         if (seq == -1)
97                                         {
98                                                 returnParameter = new ParameterInfoImpl(this, seq, parameter);
99                                         }
100                                         else
101                                         {
102                                                 parameters[seq] = new ParameterInfoImpl(this, seq, parameter);
103                                         }
104                                 }
105                                 for (int i = 0; i < parameters.Length; i++)
106                                 {
107                                         if (parameters[i] == null)
108                                         {
109                                                 parameters[i] = new ParameterInfoImpl(this, i, -1);
110                                         }
111                                 }
112                                 if (returnParameter == null)
113                                 {
114                                         returnParameter = new ParameterInfoImpl(this, -1, -1);
115                                 }
116                         }
117                 }
118
119                 internal override int ParameterCount
120                 {
121                         get { return this.MethodSignature.GetParameterCount(); }
122                 }
123
124                 public override ParameterInfo ReturnParameter
125                 {
126                         get
127                         {
128                                 PopulateParameters();
129                                 return returnParameter;
130                         }
131                 }
132
133                 public override Type ReturnType
134                 {
135                         get
136                         {
137                                 return this.ReturnParameter.ParameterType;
138                         }
139                 }
140
141                 public override Type DeclaringType
142                 {
143                         get { return declaringType.IsModulePseudoType ? null : declaringType; }
144                 }
145
146                 public override string Name
147                 {
148                         get { return module.GetString(module.MethodDef.records[index].Name); }
149                 }
150
151                 public override int MetadataToken
152                 {
153                         get { return (MethodDefTable.Index << 24) + index + 1; }
154                 }
155
156                 public override bool IsGenericMethodDefinition
157                 {
158                         get
159                         {
160                                 PopulateGenericArguments();
161                                 return typeArgs.Length > 0;
162                         }
163                 }
164
165                 public override bool IsGenericMethod
166                 {
167                         get { return IsGenericMethodDefinition; }
168                 }
169
170                 public override Type[] GetGenericArguments()
171                 {
172                         PopulateGenericArguments();
173                         return Util.Copy(typeArgs);
174                 }
175
176                 private void PopulateGenericArguments()
177                 {
178                         if (typeArgs == null)
179                         {
180                                 int token = this.MetadataToken;
181                                 int first = module.GenericParam.FindFirstByOwner(token);
182                                 if (first == -1)
183                                 {
184                                         typeArgs = Type.EmptyTypes;
185                                 }
186                                 else
187                                 {
188                                         List<Type> list = new List<Type>();
189                                         int len = module.GenericParam.records.Length;
190                                         for (int i = first; i < len && module.GenericParam.records[i].Owner == token; i++)
191                                         {
192                                                 list.Add(new GenericTypeParameter(module, i));
193                                         }
194                                         typeArgs = list.ToArray();
195                                 }
196                         }
197                 }
198
199                 internal override Type GetGenericMethodArgument(int index)
200                 {
201                         PopulateGenericArguments();
202                         return typeArgs[index];
203                 }
204
205                 internal override int GetGenericMethodArgumentCount()
206                 {
207                         PopulateGenericArguments();
208                         return typeArgs.Length;
209                 }
210
211                 public override MethodInfo GetGenericMethodDefinition()
212                 {
213                         if (this.IsGenericMethodDefinition)
214                         {
215                                 return this;
216                         }
217                         throw new InvalidOperationException();
218                 }
219
220                 public override MethodInfo MakeGenericMethod(params Type[] typeArguments)
221                 {
222                         return new GenericMethodInstance(declaringType, this, typeArguments);
223                 }
224
225                 public override Module Module
226                 {
227                         get { return module; }
228                 }
229
230                 internal override IList<CustomAttributeData> GetCustomAttributesData(Type attributeType)
231                 {
232                         List<CustomAttributeData> list = module.GetCustomAttributes(this.MetadataToken, attributeType);
233                         if ((this.Attributes & MethodAttributes.PinvokeImpl) != 0
234                                 && (attributeType == null || attributeType.IsAssignableFrom(module.universe.System_Runtime_InteropServices_DllImportAttribute)))
235                         {
236                                 CreateDllImportPseudoCustomAttribute(list);
237                         }
238                         return list;
239                 }
240
241                 private void CreateDllImportPseudoCustomAttribute(List<CustomAttributeData> attribs)
242                 {
243                         int token = this.MetadataToken;
244                         // TODO use binary search?
245                         for (int i = 0; i < module.ImplMap.records.Length; i++)
246                         {
247                                 if (module.ImplMap.records[i].MemberForwarded == token)
248                                 {
249                                         const short NoMangle = 0x0001;
250                                         const short CharSetMask = 0x0006;
251                                         const short CharSetNotSpec = 0x0000;
252                                         const short CharSetAnsi = 0x0002;
253                                         const short CharSetUnicode = 0x0004;
254                                         const short CharSetAuto = 0x0006;
255                                         const short SupportsLastError = 0x0040;
256                                         const short CallConvMask = 0x0700;
257                                         const short CallConvWinapi = 0x0100;
258                                         const short CallConvCdecl = 0x0200;
259                                         const short CallConvStdcall = 0x0300;
260                                         const short CallConvThiscall = 0x0400;
261                                         const short CallConvFastcall = 0x0500;
262                                         // non-standard flags
263                                         const short BestFitOn = 0x0010;
264                                         const short BestFitOff = 0x0020;
265                                         const short CharMapErrorOn = 0x1000;
266                                         const short CharMapErrorOff = 0x2000;
267
268                                         Type type = module.universe.System_Runtime_InteropServices_DllImportAttribute;
269                                         ConstructorInfo constructor = type.GetConstructor(new Type[] { module.universe.System_String });
270                                         List<CustomAttributeNamedArgument> list = new List<CustomAttributeNamedArgument>();
271                                         int flags = module.ImplMap.records[i].MappingFlags;
272                                         string entryPoint = module.GetString(module.ImplMap.records[i].ImportName);
273                                         string dllName = module.GetString(module.ModuleRef.records[(module.ImplMap.records[i].ImportScope & 0xFFFFFF) - 1]);
274                                         System.Runtime.InteropServices.CharSet? charSet;
275                                         switch (flags & CharSetMask)
276                                         {
277                                                 case CharSetAnsi:
278                                                         charSet = System.Runtime.InteropServices.CharSet.Ansi;
279                                                         break;
280                                                 case CharSetUnicode:
281                                                         charSet = System.Runtime.InteropServices.CharSet.Unicode;
282                                                         break;
283                                                 case CharSetAuto:
284                                                         charSet = System.Runtime.InteropServices.CharSet.Auto;
285                                                         break;
286                                                 case CharSetNotSpec:
287                                                 default:
288                                                         charSet = null;
289                                                         break;
290                                         }
291                                         System.Runtime.InteropServices.CallingConvention callingConvention;
292                                         switch (flags & CallConvMask)
293                                         {
294                                                 case CallConvCdecl:
295                                                         callingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl;
296                                                         break;
297                                                 case CallConvFastcall:
298                                                         callingConvention = System.Runtime.InteropServices.CallingConvention.FastCall;
299                                                         break;
300                                                 case CallConvStdcall:
301                                                         callingConvention = System.Runtime.InteropServices.CallingConvention.StdCall;
302                                                         break;
303                                                 case CallConvThiscall:
304                                                         callingConvention = System.Runtime.InteropServices.CallingConvention.ThisCall;
305                                                         break;
306                                                 case CallConvWinapi:
307                                                 default:
308                                                         callingConvention = System.Runtime.InteropServices.CallingConvention.Winapi;
309                                                         break;
310                                         }
311                                         list.Add(MakeNamedArgument(type, "EntryPoint", entryPoint));
312                                         list.Add(MakeNamedArgument(type, "ExactSpelling", flags, NoMangle));
313                                         list.Add(MakeNamedArgument(type, "SetLastError", flags, SupportsLastError));
314                                         list.Add(MakeNamedArgument(type, "PreserveSig", (int)GetMethodImplementationFlags(), (int)MethodImplAttributes.PreserveSig));
315                                         list.Add(MakeNamedArgument(type, "CallingConvention", (int)callingConvention));
316                                         if (charSet.HasValue)
317                                         {
318                                                 list.Add(MakeNamedArgument(type, "CharSet", (int)charSet.Value));
319                                         }
320                                         if ((flags & (BestFitOn | BestFitOff)) != 0)
321                                         {
322                                                 list.Add(MakeNamedArgument(type, "BestFitMapping", flags, BestFitOn));
323                                         }
324                                         if ((flags & (CharMapErrorOn | CharMapErrorOff)) != 0)
325                                         {
326                                                 list.Add(MakeNamedArgument(type, "ThrowOnUnmappableChar", flags, CharMapErrorOn));
327                                         }
328                                         attribs.Add(new CustomAttributeData(constructor, new object[] { dllName }, list));
329                                         return;
330                                 }
331                         }
332                 }
333
334                 private static CustomAttributeNamedArgument MakeNamedArgument(Type type, string field, string value)
335                 {
336                         return new CustomAttributeNamedArgument(type.GetField(field), new CustomAttributeTypedArgument(type.Module.universe.System_String, value));
337                 }
338
339                 private static CustomAttributeNamedArgument MakeNamedArgument(Type type, string field, int value)
340                 {
341                         return new CustomAttributeNamedArgument(type.GetField(field), new CustomAttributeTypedArgument(type.Module.universe.System_Int32, value));
342                 }
343
344                 private static CustomAttributeNamedArgument MakeNamedArgument(Type type, string field, int flags, int flagMask)
345                 {
346                         return new CustomAttributeNamedArgument(type.GetField(field), new CustomAttributeTypedArgument(type.Module.universe.System_Boolean, (flags & flagMask) != 0));
347                 }
348
349                 internal override MethodSignature MethodSignature
350                 {
351                         get { return lazyMethodSignature ?? (lazyMethodSignature = MethodSignature.ReadSig(module, module.GetBlob(module.MethodDef.records[index].Signature), this)); }
352                 }
353
354                 internal override int ImportTo(Emit.ModuleBuilder module)
355                 {
356                         return module.ImportMethodOrField(declaringType, this.Name, this.MethodSignature);
357                 }
358         }
359
360         sealed class ParameterInfoImpl : ParameterInfo
361         {
362                 private readonly MethodDefImpl method;
363                 private readonly int position;
364                 private readonly int index;
365
366                 internal ParameterInfoImpl(MethodDefImpl method, int position, int index)
367                 {
368                         this.method = method;
369                         this.position = position;
370                         this.index = index;
371                 }
372
373                 public override string Name
374                 {
375                         get { return index == -1 ? null : ((ModuleReader)this.Module).GetString(this.Module.Param.records[index].Name); }
376                 }
377
378                 public override Type ParameterType
379                 {
380                         get { return position == -1 ? method.MethodSignature.GetReturnType(method) : method.MethodSignature.GetParameterType(method, position); }
381                 }
382
383                 public override ParameterAttributes Attributes
384                 {
385                         get { return index == -1 ? ParameterAttributes.None : (ParameterAttributes)this.Module.Param.records[index].Flags; }
386                 }
387
388                 public override int Position
389                 {
390                         get { return position; }
391                 }
392
393                 public override object RawDefaultValue
394                 {
395                         get
396                         {
397                                 if ((this.Attributes & ParameterAttributes.HasDefault) != 0)
398                                 {
399                                         return this.Module.Constant.GetRawConstantValue(this.Module, this.MetadataToken);
400                                 }
401                                 Universe universe = this.Module.universe;
402                                 if (this.ParameterType == universe.System_Decimal)
403                                 {
404                                         Type attr = universe.System_Runtime_CompilerServices_DecimalConstantAttribute;
405                                         if (attr != null)
406                                         {
407                                                 foreach (CustomAttributeData cad in GetCustomAttributesData(attr))
408                                                 {
409                                                         IList<CustomAttributeTypedArgument> args = cad.ConstructorArguments;
410                                                         if (args.Count == 5)
411                                                         {
412                                                                 if (args[0].ArgumentType == universe.System_Byte
413                                                                         && args[1].ArgumentType == universe.System_Byte
414                                                                         && args[2].ArgumentType == universe.System_Int32
415                                                                         && args[3].ArgumentType == universe.System_Int32
416                                                                         && args[4].ArgumentType == universe.System_Int32)
417                                                                 {
418                                                                         return new Decimal((int)args[4].Value, (int)args[3].Value, (int)args[2].Value, (byte)args[1].Value != 0, (byte)args[0].Value);
419                                                                 }
420                                                                 else if (args[0].ArgumentType == universe.System_Byte
421                                                                         && args[1].ArgumentType == universe.System_Byte
422                                                                         && args[2].ArgumentType == universe.System_UInt32
423                                                                         && args[3].ArgumentType == universe.System_UInt32
424                                                                         && args[4].ArgumentType == universe.System_UInt32)
425                                                                 {
426                                                                         return new Decimal(unchecked((int)(uint)args[4].Value), unchecked((int)(uint)args[3].Value), unchecked((int)(uint)args[2].Value), (byte)args[1].Value != 0, (byte)args[0].Value);
427                                                                 }
428                                                         }
429                                                 }
430                                         }
431                                 }
432                                 if ((this.Attributes & ParameterAttributes.Optional) != 0)
433                                 {
434                                         return Missing.Value;
435                                 }
436                                 return null;
437                         }
438                 }
439
440                 public override Type[] GetRequiredCustomModifiers()
441                 {
442                         return Util.Copy(position == -1 ? method.MethodSignature.GetReturnTypeRequiredCustomModifiers(method) : method.MethodSignature.GetParameterRequiredCustomModifiers(method, position));
443                 }
444
445                 public override Type[] GetOptionalCustomModifiers()
446                 {
447                         return Util.Copy(position == -1 ? method.MethodSignature.GetReturnTypeOptionalCustomModifiers(method) : method.MethodSignature.GetParameterOptionalCustomModifiers(method, position));
448                 }
449
450                 public override MemberInfo Member
451                 {
452                         get
453                         {
454                                 // return the right ConstructorInfo wrapper
455                                 return method.Module.ResolveMethod(method.MetadataToken);
456                         }
457                 }
458
459                 public override int MetadataToken
460                 {
461                         get
462                         {
463                                 // for parameters that don't have a row in the Param table, we return 0x08000000 (because index is -1 in that case),
464                                 // just like .NET
465                                 return (ParamTable.Index << 24) + index + 1;
466                         }
467                 }
468
469                 internal override Module Module
470                 {
471                         get { return method.Module; }
472                 }
473
474                 internal override IList<CustomAttributeData> GetCustomAttributesData(Type attributeType)
475                 {
476                         IList<CustomAttributeData> list = base.GetCustomAttributesData(attributeType);
477                         if ((this.Attributes & ParameterAttributes.HasFieldMarshal) != 0
478                                 && (attributeType == null || attributeType.IsAssignableFrom(this.Module.universe.System_Runtime_InteropServices_MarshalAsAttribute)))
479                         {
480                                 list.Add(MarshalSpec.GetMarshalAsAttribute(this.Module, this.MetadataToken));
481                         }
482                         return list;
483                 }
484         }
485 }