Prevent race conditions for this cached variable, by using a temporary variable,...
[mono.git] / mcs / class / IKVM.Reflection / MethodSignature.cs
1 /*
2   Copyright (C) 2009-2011 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.Diagnostics;
27 using IKVM.Reflection.Reader;
28 using IKVM.Reflection.Writer;
29 using IKVM.Reflection.Emit;
30
31 namespace IKVM.Reflection
32 {
33         sealed class MethodSignature : Signature
34         {
35                 private readonly Type returnType;
36                 private readonly Type[] parameterTypes;
37                 private readonly PackedCustomModifiers modifiers;
38                 private readonly CallingConventions callingConvention;
39                 private readonly int genericParamCount;
40
41                 internal MethodSignature(Type returnType, Type[] parameterTypes, PackedCustomModifiers modifiers, CallingConventions callingConvention, int genericParamCount)
42                 {
43                         this.returnType = returnType;
44                         this.parameterTypes = parameterTypes;
45                         this.modifiers = modifiers;
46                         this.callingConvention = callingConvention;
47                         this.genericParamCount = genericParamCount;
48                 }
49
50                 public override bool Equals(object obj)
51                 {
52                         MethodSignature other = obj as MethodSignature;
53                         return other != null
54                                 && other.callingConvention == callingConvention
55                                 && other.genericParamCount == genericParamCount
56                                 && other.returnType.Equals(returnType)
57                                 && Util.ArrayEquals(other.parameterTypes, parameterTypes)
58                                 && other.modifiers.Equals(modifiers);
59                 }
60
61                 public override int GetHashCode()
62                 {
63                         return genericParamCount ^ 77 * (int)callingConvention
64                                 ^ 3 * returnType.GetHashCode()
65                                 ^ Util.GetHashCode(parameterTypes) * 5
66                                 ^ modifiers.GetHashCode() * 55;
67                 }
68
69                 private sealed class UnboundGenericMethodContext : IGenericContext
70                 {
71                         private readonly IGenericContext original;
72
73                         internal UnboundGenericMethodContext(IGenericContext original)
74                         {
75                                 this.original = original;
76                         }
77
78                         public Type GetGenericTypeArgument(int index)
79                         {
80                                 return original.GetGenericTypeArgument(index);
81                         }
82
83                         public Type GetGenericMethodArgument(int index)
84                         {
85                                 return UnboundGenericMethodParameter.Make(index);
86                         }
87                 }
88
89                 internal static MethodSignature ReadSig(ModuleReader module, ByteReader br, IGenericContext context)
90                 {
91                         CallingConventions callingConvention;
92                         int genericParamCount;
93                         Type returnType;
94                         Type[] parameterTypes;
95                         byte flags = br.ReadByte();
96                         switch (flags & 7)
97                         {
98                                 case DEFAULT:
99                                         callingConvention = CallingConventions.Standard;
100                                         break;
101                                 case VARARG:
102                                         callingConvention = CallingConventions.VarArgs;
103                                         break;
104                                 default:
105                                         throw new BadImageFormatException();
106                         }
107                         if ((flags & HASTHIS) != 0)
108                         {
109                                 callingConvention |= CallingConventions.HasThis;
110                         }
111                         if ((flags & EXPLICITTHIS) != 0)
112                         {
113                                 callingConvention |= CallingConventions.ExplicitThis;
114                         }
115                         genericParamCount = 0;
116                         if ((flags & GENERIC) != 0)
117                         {
118                                 genericParamCount = br.ReadCompressedInt();
119                                 context = new UnboundGenericMethodContext(context);
120                         }
121                         int paramCount = br.ReadCompressedInt();
122                         CustomModifiers[] modifiers = null;
123                         PackedCustomModifiers.Pack(ref modifiers, 0, CustomModifiers.Read(module, br, context), paramCount + 1);
124                         returnType = ReadRetType(module, br, context);
125                         parameterTypes = new Type[paramCount];
126                         for (int i = 0; i < parameterTypes.Length; i++)
127                         {
128                                 if ((callingConvention & CallingConventions.VarArgs) != 0 && br.PeekByte() == SENTINEL)
129                                 {
130                                         Array.Resize(ref parameterTypes, i);
131                                         if (modifiers != null)
132                                         {
133                                                 Array.Resize(ref modifiers, i + 1);
134                                         }
135                                         break;
136                                 }
137                                 PackedCustomModifiers.Pack(ref modifiers, i + 1, CustomModifiers.Read(module, br, context), paramCount + 1);
138                                 parameterTypes[i] = ReadParam(module, br, context);
139                         }
140                         return new MethodSignature(returnType, parameterTypes, PackedCustomModifiers.Wrap(modifiers), callingConvention, genericParamCount);
141                 }
142
143                 internal static __StandAloneMethodSig ReadStandAloneMethodSig(ModuleReader module, ByteReader br, IGenericContext context)
144                 {
145                         CallingConventions callingConvention = 0;
146                         System.Runtime.InteropServices.CallingConvention unmanagedCallingConvention = 0;
147                         bool unmanaged;
148                         byte flags = br.ReadByte();
149                         switch (flags & 7)
150                         {
151                                 case DEFAULT:
152                                         callingConvention = CallingConventions.Standard;
153                                         unmanaged = false;
154                                         break;
155                                 case 0x01:      // C
156                                         unmanagedCallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl;
157                                         unmanaged = true;
158                                         break;
159                                 case 0x02:      // STDCALL
160                                         unmanagedCallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall;
161                                         unmanaged = true;
162                                         break;
163                                 case 0x03:      // THISCALL
164                                         unmanagedCallingConvention = System.Runtime.InteropServices.CallingConvention.ThisCall;
165                                         unmanaged = true;
166                                         break;
167                                 case 0x04:      // FASTCALL
168                                         unmanagedCallingConvention = System.Runtime.InteropServices.CallingConvention.FastCall;
169                                         unmanaged = true;
170                                         break;
171                                 case VARARG:
172                                         callingConvention = CallingConventions.VarArgs;
173                                         unmanaged = false;
174                                         break;
175                                 default:
176                                         throw new BadImageFormatException();
177                         }
178                         if ((flags & HASTHIS) != 0)
179                         {
180                                 callingConvention |= CallingConventions.HasThis;
181                         }
182                         if ((flags & EXPLICITTHIS) != 0)
183                         {
184                                 callingConvention |= CallingConventions.ExplicitThis;
185                         }
186                         if ((flags & GENERIC) != 0)
187                         {
188                                 throw new BadImageFormatException();
189                         }
190                         int paramCount = br.ReadCompressedInt();
191                         CustomModifiers[] customModifiers = null;
192                         PackedCustomModifiers.Pack(ref customModifiers, 0, CustomModifiers.Read(module, br, context), paramCount + 1);
193                         Type returnType = ReadRetType(module, br, context);
194                         List<Type> parameterTypes = new List<Type>();
195                         List<Type> optionalParameterTypes = new List<Type>();
196                         List<Type> curr = parameterTypes;
197                         for (int i = 0; i < paramCount; i++)
198                         {
199                                 if (br.PeekByte() == SENTINEL)
200                                 {
201                                         br.ReadByte();
202                                         curr = optionalParameterTypes;
203                                 }
204                                 PackedCustomModifiers.Pack(ref customModifiers, i + 1, CustomModifiers.Read(module, br, context), paramCount + 1);
205                                 curr.Add(ReadParam(module, br, context));
206                         }
207                         return new __StandAloneMethodSig(unmanaged, unmanagedCallingConvention, callingConvention, returnType, parameterTypes.ToArray(), optionalParameterTypes.ToArray(), PackedCustomModifiers.Wrap(customModifiers));
208                 }
209
210                 internal int GetParameterCount()
211                 {
212                         return parameterTypes.Length;
213                 }
214
215                 internal Type GetParameterType(int index)
216                 {
217                         return parameterTypes[index];
218                 }
219
220                 internal Type GetReturnType(IGenericBinder binder)
221                 {
222                         return returnType.BindTypeParameters(binder);
223                 }
224
225                 internal CustomModifiers GetReturnTypeCustomModifiers(IGenericBinder binder)
226                 {
227                         return modifiers.GetReturnTypeCustomModifiers().Bind(binder);
228                 }
229
230                 internal Type GetParameterType(IGenericBinder binder, int index)
231                 {
232                         return parameterTypes[index].BindTypeParameters(binder);
233                 }
234
235                 internal CustomModifiers GetParameterCustomModifiers(IGenericBinder binder, int index)
236                 {
237                         return modifiers.GetParameterCustomModifiers(index).Bind(binder);
238                 }
239
240                 internal CallingConventions CallingConvention
241                 {
242                         get { return callingConvention; }
243                 }
244
245                 internal int GenericParameterCount
246                 {
247                         get { return genericParamCount; }
248                 }
249
250                 private sealed class Binder : IGenericBinder
251                 {
252                         private readonly Type declaringType;
253                         private readonly Type[] methodArgs;
254
255                         internal Binder(Type declaringType, Type[] methodArgs)
256                         {
257                                 this.declaringType = declaringType;
258                                 this.methodArgs = methodArgs;
259                         }
260
261                         public Type BindTypeParameter(Type type)
262                         {
263                                 return declaringType.GetGenericTypeArgument(type.GenericParameterPosition);
264                         }
265
266                         public Type BindMethodParameter(Type type)
267                         {
268                                 if (methodArgs == null)
269                                 {
270                                         return type;
271                                 }
272                                 return methodArgs[type.GenericParameterPosition];
273                         }
274                 }
275
276                 internal MethodSignature Bind(Type type, Type[] methodArgs)
277                 {
278                         Binder binder = new Binder(type, methodArgs);
279                         return new MethodSignature(returnType.BindTypeParameters(binder),
280                                 BindTypeParameters(binder, parameterTypes),
281                                 modifiers.Bind(binder),
282                                 callingConvention, genericParamCount);
283                 }
284
285                 private sealed class Unbinder : IGenericBinder
286                 {
287                         internal static readonly Unbinder Instance = new Unbinder();
288
289                         private Unbinder()
290                         {
291                         }
292
293                         public Type BindTypeParameter(Type type)
294                         {
295                                 return type;
296                         }
297
298                         public Type BindMethodParameter(Type type)
299                         {
300                                 return UnboundGenericMethodParameter.Make(type.GenericParameterPosition);
301                         }
302                 }
303
304                 internal static MethodSignature MakeFromBuilder(Type returnType, Type[] parameterTypes, PackedCustomModifiers modifiers, CallingConventions callingConvention, int genericParamCount)
305                 {
306                         if (genericParamCount > 0)
307                         {
308                                 returnType = returnType.BindTypeParameters(Unbinder.Instance);
309                                 parameterTypes = BindTypeParameters(Unbinder.Instance, parameterTypes);
310                                 modifiers = modifiers.Bind(Unbinder.Instance);
311                         }
312                         return new MethodSignature(returnType, parameterTypes, modifiers, callingConvention, genericParamCount);
313                 }
314
315                 internal bool MatchParameterTypes(Type[] types)
316                 {
317                         if (types == parameterTypes)
318                         {
319                                 return true;
320                         }
321                         if (types == null)
322                         {
323                                 return parameterTypes.Length == 0;
324                         }
325                         if (parameterTypes == null)
326                         {
327                                 return types.Length == 0;
328                         }
329                         if (types.Length == parameterTypes.Length)
330                         {
331                                 for (int i = 0; i < types.Length; i++)
332                                 {
333                                         if (!Util.TypeEquals(types[i], parameterTypes[i]))
334                                         {
335                                                 return false;
336                                         }
337                                 }
338                                 return true;
339                         }
340                         return false;
341                 }
342
343                 internal override void WriteSig(ModuleBuilder module, ByteBuffer bb)
344                 {
345                         WriteSigImpl(module, bb, parameterTypes.Length);
346                 }
347
348                 internal void WriteMethodRefSig(ModuleBuilder module, ByteBuffer bb, Type[] optionalParameterTypes, CustomModifiers[] customModifiers)
349                 {
350                         WriteSigImpl(module, bb, parameterTypes.Length + optionalParameterTypes.Length);
351                         if (optionalParameterTypes.Length > 0)
352                         {
353                                 bb.Write(SENTINEL);
354                                 for (int i = 0; i < optionalParameterTypes.Length; i++)
355                                 {
356                                         WriteCustomModifiers(module, bb, Util.NullSafeElementAt(customModifiers, i));
357                                         WriteType(module, bb, optionalParameterTypes[i]);
358                                 }
359                         }
360                 }
361
362                 private void WriteSigImpl(ModuleBuilder module, ByteBuffer bb, int parameterCount)
363                 {
364                         byte first;
365                         if ((callingConvention & CallingConventions.Any) == CallingConventions.VarArgs)
366                         {
367                                 Debug.Assert(genericParamCount == 0);
368                                 first = VARARG;
369                         }
370                         else if (genericParamCount > 0)
371                         {
372                                 first = GENERIC;
373                         }
374                         else
375                         {
376                                 first = DEFAULT;
377                         }
378                         if ((callingConvention & CallingConventions.HasThis) != 0)
379                         {
380                                 first |= HASTHIS;
381                         }
382                         if ((callingConvention & CallingConventions.ExplicitThis) != 0)
383                         {
384                                 first |= EXPLICITTHIS;
385                         }
386                         bb.Write(first);
387                         if (genericParamCount > 0)
388                         {
389                                 bb.WriteCompressedInt(genericParamCount);
390                         }
391                         bb.WriteCompressedInt(parameterCount);
392                         // RetType
393                         WriteCustomModifiers(module, bb, modifiers.GetReturnTypeCustomModifiers());
394                         WriteType(module, bb, returnType);
395                         // Param
396                         for (int i = 0; i < parameterTypes.Length; i++)
397                         {
398                                 WriteCustomModifiers(module, bb, modifiers.GetParameterCustomModifiers(i));
399                                 WriteType(module, bb, parameterTypes[i]);
400                         }
401                 }
402         }
403
404         struct PackedCustomModifiers
405         {
406                 // element 0 is the return type, the rest are the parameters
407                 private readonly CustomModifiers[] customModifiers;
408
409                 private PackedCustomModifiers(CustomModifiers[] customModifiers)
410                 {
411                         this.customModifiers = customModifiers;
412                 }
413
414                 public override int GetHashCode()
415                 {
416                         return Util.GetHashCode(customModifiers);
417                 }
418
419                 public override bool Equals(object obj)
420                 {
421                         PackedCustomModifiers? other = obj as PackedCustomModifiers?;
422                         return other != null && Equals(other.Value);
423                 }
424
425                 internal bool Equals(PackedCustomModifiers other)
426                 {
427                         return Util.ArrayEquals(customModifiers, other.customModifiers);
428                 }
429
430                 internal CustomModifiers GetReturnTypeCustomModifiers()
431                 {
432                         if (customModifiers == null)
433                         {
434                                 return new CustomModifiers();
435                         }
436                         return customModifiers[0];
437                 }
438
439                 internal CustomModifiers GetParameterCustomModifiers(int index)
440                 {
441                         if (customModifiers == null)
442                         {
443                                 return new CustomModifiers();
444                         }
445                         return customModifiers[index + 1];
446                 }
447
448                 internal PackedCustomModifiers Bind(IGenericBinder binder)
449                 {
450                         if (customModifiers == null)
451                         {
452                                 return new PackedCustomModifiers();
453                         }
454                         CustomModifiers[] expanded = new CustomModifiers[customModifiers.Length];
455                         for (int i = 0; i < customModifiers.Length; i++)
456                         {
457                                 expanded[i] = customModifiers[i].Bind(binder);
458                         }
459                         return new PackedCustomModifiers(expanded);
460                 }
461
462                 // this method make a copy of the incoming arrays (where necessary) and returns a normalized modifiers array
463                 internal static PackedCustomModifiers CreateFromExternal(Type[] returnOptional, Type[] returnRequired, Type[][] parameterOptional, Type[][] parameterRequired, int parameterCount)
464                 {
465                         CustomModifiers[] modifiers = null;
466                         Pack(ref modifiers, 0, CustomModifiers.FromReqOpt(returnRequired, returnOptional), parameterCount + 1);
467                         for (int i = 0; i < parameterCount; i++)
468                         {
469                                 Pack(ref modifiers, i + 1, CustomModifiers.FromReqOpt(Util.NullSafeElementAt(parameterRequired, i), Util.NullSafeElementAt(parameterOptional, i)), parameterCount + 1);
470                         }
471                         return new PackedCustomModifiers(modifiers);
472                 }
473
474                 internal static PackedCustomModifiers CreateFromExternal(CustomModifiers returnTypeCustomModifiers, CustomModifiers[] parameterTypeCustomModifiers, int parameterCount)
475                 {
476                         CustomModifiers[] customModifiers = null;
477                         Pack(ref customModifiers, 0, returnTypeCustomModifiers, parameterCount + 1);
478                         if (parameterTypeCustomModifiers != null)
479                         {
480                                 for (int i = 0; i < parameterCount; i++)
481                                 {
482                                         Pack(ref customModifiers, i + 1, parameterTypeCustomModifiers[i], parameterCount + 1);
483                                 }
484                         }
485                         return new PackedCustomModifiers(customModifiers);
486                 }
487
488                 internal static PackedCustomModifiers Wrap(CustomModifiers[] modifiers)
489                 {
490                         return new PackedCustomModifiers(modifiers);
491                 }
492
493                 internal static void Pack(ref CustomModifiers[] array, int index, CustomModifiers mods, int count)
494                 {
495                         if (!mods.IsEmpty)
496                         {
497                                 if (array == null)
498                                 {
499                                         array = new CustomModifiers[count];
500                                 }
501                                 array[index] = mods;
502                         }
503                 }
504         }
505 }