Merged into single file, added assertions
[mono.git] / mcs / class / IKVM.Reflection / Emit / MethodBuilder.cs
1 /*
2   Copyright (C) 2008-2012 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.IO;
26 using System.Diagnostics;
27 using System.Collections.Generic;
28 using System.Runtime.InteropServices;
29 using System.Runtime.CompilerServices;
30 using System.Diagnostics.SymbolStore;
31 using IKVM.Reflection.Metadata;
32 using IKVM.Reflection.Writer;
33
34 namespace IKVM.Reflection.Emit
35 {
36         public sealed class MethodBuilder : MethodInfo
37         {
38                 private readonly TypeBuilder typeBuilder;
39                 private readonly string name;
40                 private readonly int pseudoToken;
41                 private int nameIndex;
42                 private int signature;
43                 private Type returnType;
44                 private Type[] parameterTypes;
45                 private PackedCustomModifiers customModifiers;
46                 private MethodAttributes attributes;
47                 private MethodImplAttributes implFlags;
48                 private ILGenerator ilgen;
49                 private int rva = -1;
50                 private CallingConventions callingConvention;
51                 private List<ParameterBuilder> parameters;
52                 private GenericTypeParameterBuilder[] gtpb;
53                 private List<CustomAttributeBuilder> declarativeSecurity;
54                 private MethodSignature methodSignature;
55                 private bool initLocals = true;
56
57                 internal MethodBuilder(TypeBuilder typeBuilder, string name, MethodAttributes attributes, CallingConventions callingConvention)
58                 {
59                         this.typeBuilder = typeBuilder;
60                         this.name = name;
61                         this.pseudoToken = typeBuilder.ModuleBuilder.AllocPseudoToken();
62                         this.attributes = attributes;
63                         if ((attributes & MethodAttributes.Static) == 0)
64                         {
65                                 callingConvention |= CallingConventions.HasThis;
66                         }
67                         this.callingConvention = callingConvention;
68                 }
69
70                 public ILGenerator GetILGenerator()
71                 {
72                         return GetILGenerator(16);
73                 }
74
75                 public ILGenerator GetILGenerator(int streamSize)
76                 {
77                         if (rva != -1)
78                         {
79                                 throw new InvalidOperationException();
80                         }
81                         if (ilgen == null)
82                         {
83                                 ilgen = new ILGenerator(typeBuilder.ModuleBuilder, streamSize);
84                         }
85                         return ilgen;
86                 }
87
88                 public void __ReleaseILGenerator()
89                 {
90                         if (ilgen != null)
91                         {
92                                 if (this.ModuleBuilder.symbolWriter != null)
93                                 {
94                                         this.ModuleBuilder.symbolWriter.OpenMethod(new SymbolToken(-pseudoToken | 0x06000000), this);
95                                 }
96                                 rva = ilgen.WriteBody(initLocals);
97                                 if (this.ModuleBuilder.symbolWriter != null)
98                                 {
99                                         this.ModuleBuilder.symbolWriter.CloseMethod();
100                                 }
101                                 ilgen = null;
102                         }
103                 }
104
105                 public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute)
106                 {
107                         SetCustomAttribute(new CustomAttributeBuilder(con, binaryAttribute));
108                 }
109
110                 private void SetDllImportPseudoCustomAttribute(CustomAttributeBuilder customBuilder)
111                 {
112                         CallingConvention? callingConvention = customBuilder.GetFieldValue<CallingConvention>("CallingConvention");
113                         CharSet? charSet = customBuilder.GetFieldValue<CharSet>("CharSet");
114                         SetDllImportPseudoCustomAttribute((string)customBuilder.GetConstructorArgument(0),
115                                 (string)customBuilder.GetFieldValue("EntryPoint"),
116                                 callingConvention,
117                                 charSet,
118                                 (bool?)customBuilder.GetFieldValue("BestFitMapping"),
119                                 (bool?)customBuilder.GetFieldValue("ThrowOnUnmappableChar"),
120                                 (bool?)customBuilder.GetFieldValue("SetLastError"),
121                                 (bool?)customBuilder.GetFieldValue("PreserveSig"),
122                                 (bool?)customBuilder.GetFieldValue("ExactSpelling"));
123                 }
124
125                 internal void SetDllImportPseudoCustomAttribute(string dllName, string entryName, CallingConvention? nativeCallConv, CharSet? nativeCharSet,
126                         bool? bestFitMapping, bool? throwOnUnmappableChar, bool? setLastError, bool? preserveSig, bool? exactSpelling)
127                 {
128                         const short NoMangle = 0x0001;
129                         const short CharSetMask = 0x0006;
130                         const short CharSetNotSpec = 0x0000;
131                         const short CharSetAnsi = 0x0002;
132                         const short CharSetUnicode = 0x0004;
133                         const short CharSetAuto = 0x0006;
134                         const short SupportsLastError = 0x0040;
135                         const short CallConvMask = 0x0700;
136                         const short CallConvWinapi = 0x0100;
137                         const short CallConvCdecl = 0x0200;
138                         const short CallConvStdcall = 0x0300;
139                         const short CallConvThiscall = 0x0400;
140                         const short CallConvFastcall = 0x0500;
141                         // non-standard flags
142                         const short BestFitOn = 0x0010;
143                         const short BestFitOff = 0x0020;
144                         const short CharMapErrorOn = 0x1000;
145                         const short CharMapErrorOff = 0x2000;
146                         short flags = CharSetNotSpec | CallConvWinapi;
147                         if (bestFitMapping.HasValue)
148                         {
149                                 flags |= bestFitMapping.Value ? BestFitOn : BestFitOff;
150                         }
151                         if (throwOnUnmappableChar.HasValue)
152                         {
153                                 flags |= throwOnUnmappableChar.Value ? CharMapErrorOn : CharMapErrorOff;
154                         }
155                         if (nativeCallConv.HasValue)
156                         {
157                                 flags &= ~CallConvMask;
158                                 switch (nativeCallConv.Value)
159                                 {
160                                         case System.Runtime.InteropServices.CallingConvention.Cdecl:
161                                                 flags |= CallConvCdecl;
162                                                 break;
163                                         case System.Runtime.InteropServices.CallingConvention.FastCall:
164                                                 flags |= CallConvFastcall;
165                                                 break;
166                                         case System.Runtime.InteropServices.CallingConvention.StdCall:
167                                                 flags |= CallConvStdcall;
168                                                 break;
169                                         case System.Runtime.InteropServices.CallingConvention.ThisCall:
170                                                 flags |= CallConvThiscall;
171                                                 break;
172                                         case System.Runtime.InteropServices.CallingConvention.Winapi:
173                                                 flags |= CallConvWinapi;
174                                                 break;
175                                 }
176                         }
177                         if (nativeCharSet.HasValue)
178                         {
179                                 flags &= ~CharSetMask;
180                                 switch (nativeCharSet.Value)
181                                 {
182                                         case CharSet.Ansi:
183                                         case CharSet.None:
184                                                 flags |= CharSetAnsi;
185                                                 break;
186                                         case CharSet.Auto:
187                                                 flags |= CharSetAuto;
188                                                 break;
189                                         case CharSet.Unicode:
190                                                 flags |= CharSetUnicode;
191                                                 break;
192                                 }
193                         }
194                         if (exactSpelling.HasValue && exactSpelling.Value)
195                         {
196                                 flags |= NoMangle;
197                         }
198                         if (!preserveSig.HasValue || preserveSig.Value)
199                         {
200                                 implFlags |= MethodImplAttributes.PreserveSig;
201                         }
202                         if (setLastError.HasValue && setLastError.Value)
203                         {
204                                 flags |= SupportsLastError;
205                         }
206                         ImplMapTable.Record rec = new ImplMapTable.Record();
207                         rec.MappingFlags = flags;
208                         rec.MemberForwarded = pseudoToken;
209                         rec.ImportName = this.ModuleBuilder.Strings.Add(entryName ?? name);
210                         rec.ImportScope = this.ModuleBuilder.ModuleRef.FindOrAddRecord(dllName == null ? 0 : this.ModuleBuilder.Strings.Add(dllName));
211                         this.ModuleBuilder.ImplMap.AddRecord(rec);
212                 }
213
214                 private void SetMethodImplAttribute(CustomAttributeBuilder customBuilder)
215                 {
216                         MethodImplOptions opt;
217                         switch (customBuilder.Constructor.ParameterCount)
218                         {
219                                 case 0:
220                                         opt = 0;
221                                         break;
222                                 case 1:
223                                         {
224                                                 object val = customBuilder.GetConstructorArgument(0);
225                                                 if (val is short)
226                                                 {
227                                                         opt = (MethodImplOptions)(short)val;
228                                                 }
229                                                 else if (val is int)
230                                                 {
231                                                         opt = (MethodImplOptions)(int)val;
232                                                 }
233                                                 else
234                                                 {
235                                                         opt = (MethodImplOptions)val;
236                                                 }
237                                                 break;
238                                         }
239                                 default:
240                                         throw new NotSupportedException();
241                         }
242                         MethodCodeType? type = customBuilder.GetFieldValue<MethodCodeType>("MethodCodeType");
243                         implFlags = (MethodImplAttributes)opt;
244                         if (type.HasValue)
245                         {
246                                 implFlags |= (MethodImplAttributes)type;
247                         }
248                 }
249
250                 public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
251                 {
252                         Universe u = this.ModuleBuilder.universe;
253                         Type type = customBuilder.Constructor.DeclaringType;
254                         if (type == u.System_Runtime_InteropServices_DllImportAttribute)
255                         {
256                                 attributes |= MethodAttributes.PinvokeImpl;
257                                 SetDllImportPseudoCustomAttribute(customBuilder.DecodeBlob(this.Module.Assembly));
258                         }
259                         else if (type == u.System_Runtime_CompilerServices_MethodImplAttribute)
260                         {
261                                 SetMethodImplAttribute(customBuilder.DecodeBlob(this.Module.Assembly));
262                         }
263                         else if (type == u.System_Runtime_InteropServices_PreserveSigAttribute)
264                         {
265                                 implFlags |= MethodImplAttributes.PreserveSig;
266                         }
267                         else if (type == u.System_Runtime_CompilerServices_SpecialNameAttribute)
268                         {
269                                 attributes |= MethodAttributes.SpecialName;
270                         }
271                         else
272                         {
273                                 if (type == u.System_Security_SuppressUnmanagedCodeSecurityAttribute)
274                                 {
275                                         attributes |= MethodAttributes.HasSecurity;
276                                 }
277                                 this.ModuleBuilder.SetCustomAttribute(pseudoToken, customBuilder);
278                         }
279                 }
280
281                 public void __AddDeclarativeSecurity(CustomAttributeBuilder customBuilder)
282                 {
283                         attributes |= MethodAttributes.HasSecurity;
284                         if (declarativeSecurity == null)
285                         {
286                                 declarativeSecurity = new List<CustomAttributeBuilder>();
287                         }
288                         declarativeSecurity.Add(customBuilder);
289                 }
290
291                 public void AddDeclarativeSecurity(System.Security.Permissions.SecurityAction securityAction, System.Security.PermissionSet permissionSet)
292                 {
293                         this.ModuleBuilder.AddDeclarativeSecurity(pseudoToken, securityAction, permissionSet);
294                         this.attributes |= MethodAttributes.HasSecurity;
295                 }
296
297                 public void SetImplementationFlags(MethodImplAttributes attributes)
298                 {
299                         implFlags = attributes;
300                 }
301
302                 public ParameterBuilder DefineParameter(int position, ParameterAttributes attributes, string strParamName)
303                 {
304                         if (parameters == null)
305                         {
306                                 parameters = new List<ParameterBuilder>();
307                         }
308                         this.ModuleBuilder.Param.AddVirtualRecord();
309                         ParameterBuilder pb = new ParameterBuilder(this.ModuleBuilder, position, attributes, strParamName);
310                         if (parameters.Count == 0 || position > parameters[parameters.Count - 1].Position)
311                         {
312                                 parameters.Add(pb);
313                         }
314                         else
315                         {
316                                 for (int i = 0; i < parameters.Count; i++)
317                                 {
318                                         if (parameters[i].Position > position)
319                                         {
320                                                 parameters.Insert(i, pb);
321                                                 break;
322                                         }
323                                 }
324                         }
325                         return pb;
326                 }
327
328                 private void CheckSig()
329                 {
330                         if (methodSignature != null)
331                         {
332                                 throw new InvalidOperationException("The method signature can not be modified after it has been used.");
333                         }
334                 }
335
336                 public void SetParameters(params Type[] parameterTypes)
337                 {
338                         CheckSig();
339                         this.parameterTypes = Util.Copy(parameterTypes);
340                 }
341
342                 public void SetReturnType(Type returnType)
343                 {
344                         CheckSig();
345                         this.returnType = returnType ?? this.Module.universe.System_Void;
346                 }
347
348                 public void SetSignature(Type returnType, Type[] returnTypeRequiredCustomModifiers, Type[] returnTypeOptionalCustomModifiers, Type[] parameterTypes, Type[][] parameterTypeRequiredCustomModifiers, Type[][] parameterTypeOptionalCustomModifiers)
349                 {
350                         SetSignature(returnType, parameterTypes, PackedCustomModifiers.CreateFromExternal(returnTypeOptionalCustomModifiers, returnTypeRequiredCustomModifiers,
351                                 parameterTypeOptionalCustomModifiers, parameterTypeRequiredCustomModifiers, Util.NullSafeLength(parameterTypes)));
352                 }
353
354                 public void __SetSignature(Type returnType, CustomModifiers returnTypeCustomModifiers, Type[] parameterTypes, CustomModifiers[] parameterTypeCustomModifiers)
355                 {
356                         SetSignature(returnType, parameterTypes, PackedCustomModifiers.CreateFromExternal(returnTypeCustomModifiers, parameterTypeCustomModifiers, Util.NullSafeLength(parameterTypes)));
357                 }
358
359                 private void SetSignature(Type returnType, Type[] parameterTypes, PackedCustomModifiers customModifiers)
360                 {
361                         CheckSig();
362                         this.returnType = returnType ?? this.Module.universe.System_Void;
363                         this.parameterTypes = Util.Copy(parameterTypes);
364                         this.customModifiers = customModifiers;
365                 }
366
367                 public GenericTypeParameterBuilder[] DefineGenericParameters(params string[] names)
368                 {
369                         CheckSig();
370                         gtpb = new GenericTypeParameterBuilder[names.Length];
371                         for (int i = 0; i < names.Length; i++)
372                         {
373                                 gtpb[i] = new GenericTypeParameterBuilder(names[i], null, this, i);
374                         }
375                         return (GenericTypeParameterBuilder[])gtpb.Clone();
376                 }
377
378                 public override MethodInfo MakeGenericMethod(params Type[] typeArguments)
379                 {
380                         return new GenericMethodInstance(typeBuilder, this, typeArguments);
381                 }
382
383                 public override MethodInfo GetGenericMethodDefinition()
384                 {
385                         if (gtpb == null)
386                         {
387                                 throw new InvalidOperationException();
388                         }
389                         return this;
390                 }
391
392                 public override Type[] GetGenericArguments()
393                 {
394                         return Util.Copy(gtpb);
395                 }
396
397                 internal override Type GetGenericMethodArgument(int index)
398                 {
399                         return gtpb[index];
400                 }
401
402                 internal override int GetGenericMethodArgumentCount()
403                 {
404                         return gtpb == null ? 0 : gtpb.Length;
405                 }
406
407                 public override Type ReturnType
408                 {
409                         get { return returnType; }
410                 }
411
412                 public override ParameterInfo ReturnParameter
413                 {
414                         get { return new ParameterInfoImpl(this, -1); }
415                 }
416
417                 public override MethodAttributes Attributes
418                 {
419                         get { return attributes; }
420                 }
421
422                 public void __SetAttributes(MethodAttributes attributes)
423                 {
424                         this.attributes = attributes;
425                 }
426
427                 public void __SetCallingConvention(CallingConventions callingConvention)
428                 {
429                         this.callingConvention = callingConvention;
430                         this.methodSignature = null;
431                 }
432
433                 public override MethodImplAttributes GetMethodImplementationFlags()
434                 {
435                         return implFlags;
436                 }
437
438                 private sealed class ParameterInfoImpl : ParameterInfo
439                 {
440                         private readonly MethodBuilder method;
441                         private readonly int parameter;
442
443                         internal ParameterInfoImpl(MethodBuilder method, int parameter)
444                         {
445                                 this.method = method;
446                                 this.parameter = parameter;
447                         }
448
449                         private ParameterBuilder ParameterBuilder
450                         {
451                                 get
452                                 {
453                                         if (method.parameters != null)
454                                         {
455                                                 foreach (ParameterBuilder pb in method.parameters)
456                                                 {
457                                                         // ParameterBuilder.Position is 1-based
458                                                         if (pb.Position - 1 == parameter)
459                                                         {
460                                                                 return pb;
461                                                         }
462                                                 }
463                                         }
464                                         return null;
465                                 }
466                         }
467
468                         public override string Name
469                         {
470                                 get
471                                 {
472                                         ParameterBuilder pb = this.ParameterBuilder;
473                                         return pb != null ? pb.Name : null;
474                                 }
475                         }
476
477                         public override Type ParameterType
478                         {
479                                 get { return parameter == -1 ? method.returnType : method.parameterTypes[parameter]; }
480                         }
481
482                         public override ParameterAttributes Attributes
483                         {
484                                 get
485                                 {
486                                         ParameterBuilder pb = this.ParameterBuilder;
487                                         return pb != null ? (ParameterAttributes)pb.Attributes : ParameterAttributes.None;
488                                 }
489                         }
490
491                         public override int Position
492                         {
493                                 get { return parameter; }
494                         }
495
496                         public override object RawDefaultValue
497                         {
498                                 get
499                                 {
500                                         ParameterBuilder pb = this.ParameterBuilder;
501                                         if (pb != null && (pb.Attributes & (int)ParameterAttributes.HasDefault) != 0)
502                                         {
503                                                 return method.ModuleBuilder.Constant.GetRawConstantValue(method.ModuleBuilder, pb.PseudoToken);
504                                         }
505                                         if (pb != null && (pb.Attributes & (int)ParameterAttributes.Optional) != 0)
506                                         {
507                                                 return Missing.Value;
508                                         }
509                                         return null;
510                                 }
511                         }
512
513                         public override CustomModifiers __GetCustomModifiers()
514                         {
515                                 return method.customModifiers.GetParameterCustomModifiers(parameter);
516                         }
517
518                         public override bool __TryGetFieldMarshal(out FieldMarshal fieldMarshal)
519                         {
520                                 fieldMarshal = new FieldMarshal();
521                                 return false;
522                         }
523
524                         public override MemberInfo Member
525                         {
526                                 get { return method; }
527                         }
528
529                         public override int MetadataToken
530                         {
531                                 get
532                                 {
533                                         ParameterBuilder pb = this.ParameterBuilder;
534                                         return pb != null ? pb.PseudoToken : 0x08000000;
535                                 }
536                         }
537
538                         internal override Module Module
539                         {
540                                 get { return method.Module; }
541                         }
542                 }
543
544                 public override ParameterInfo[] GetParameters()
545                 {
546                         ParameterInfo[] parameters = new ParameterInfo[parameterTypes.Length];
547                         for (int i = 0; i < parameters.Length; i++)
548                         {
549                                 parameters[i] = new ParameterInfoImpl(this, i);
550                         }
551                         return parameters;
552                 }
553
554                 internal override int ParameterCount
555                 {
556                         get { return parameterTypes.Length; }
557                 }
558
559                 public override Type DeclaringType
560                 {
561                         get { return typeBuilder.IsModulePseudoType ? null : typeBuilder; }
562                 }
563
564                 public override string Name
565                 {
566                         get { return name; }
567                 }
568
569                 public override CallingConventions CallingConvention
570                 {
571                         get { return callingConvention; }
572                 }
573
574                 public override int MetadataToken
575                 {
576                         get { return pseudoToken; }
577                 }
578
579                 public override bool IsGenericMethod
580                 {
581                         get { return gtpb != null; }
582                 }
583
584                 public override bool IsGenericMethodDefinition
585                 {
586                         get { return gtpb != null; }
587                 }
588
589                 public override Module Module
590                 {
591                         get { return typeBuilder.Module; }
592                 }
593
594                 public Module GetModule()
595                 {
596                         return typeBuilder.Module;
597                 }
598
599                 public MethodToken GetToken()
600                 {
601                         return new MethodToken(pseudoToken);
602                 }
603
604                 public override MethodBody GetMethodBody()
605                 {
606                         throw new NotSupportedException();
607                 }
608
609                 public override int __MethodRVA
610                 {
611                         get { throw new NotImplementedException(); }
612                 }
613
614                 public bool InitLocals
615                 {
616                         get { return initLocals; }
617                         set { initLocals = value; }
618                 }
619
620                 public void __AddUnmanagedExport(string name, int ordinal)
621                 {
622                         this.ModuleBuilder.AddUnmanagedExport(name, ordinal, this, new RelativeVirtualAddress(0xFFFFFFFF));
623                 }
624
625                 internal void Bake()
626                 {
627                         this.nameIndex = this.ModuleBuilder.Strings.Add(name);
628                         this.signature = this.ModuleBuilder.GetSignatureBlobIndex(this.MethodSignature);
629
630                         __ReleaseILGenerator();
631
632                         if (declarativeSecurity != null)
633                         {
634                                 this.ModuleBuilder.AddDeclarativeSecurity(pseudoToken, declarativeSecurity);
635                         }
636                 }
637
638                 internal ModuleBuilder ModuleBuilder
639                 {
640                         get { return typeBuilder.ModuleBuilder; }
641                 }
642
643                 internal void WriteMethodDefRecord(int baseRVA, MetadataWriter mw, ref int paramList)
644                 {
645                         if (rva != -1)
646                         {
647                                 mw.Write(rva + baseRVA);
648                         }
649                         else
650                         {
651                                 mw.Write(0);
652                         }
653                         mw.Write((short)implFlags);
654                         mw.Write((short)attributes);
655                         mw.WriteStringIndex(nameIndex);
656                         mw.WriteBlobIndex(signature);
657                         mw.WriteParam(paramList);
658                         if (parameters != null)
659                         {
660                                 paramList += parameters.Count;
661                         }
662                 }
663
664                 internal void WriteParamRecords(MetadataWriter mw)
665                 {
666                         if (parameters != null)
667                         {
668                                 foreach (ParameterBuilder pb in parameters)
669                                 {
670                                         pb.WriteParamRecord(mw);
671                                 }
672                         }
673                 }
674
675                 internal void FixupToken(int token, ref int parameterToken)
676                 {
677                         typeBuilder.ModuleBuilder.RegisterTokenFixup(this.pseudoToken, token);
678                         if (parameters != null)
679                         {
680                                 foreach (ParameterBuilder pb in parameters)
681                                 {
682                                         pb.FixupToken(parameterToken++);
683                                 }
684                         }
685                 }
686
687                 internal override MethodSignature MethodSignature
688                 {
689                         get
690                         {
691                                 if (methodSignature == null)
692                                 {
693                                         methodSignature = MethodSignature.MakeFromBuilder(returnType, parameterTypes, customModifiers, callingConvention, gtpb == null ? 0 : gtpb.Length);
694                                 }
695                                 return methodSignature;
696                         }
697                 }
698
699                 internal override int ImportTo(ModuleBuilder other)
700                 {
701                         return other.ImportMethodOrField(typeBuilder, name, this.MethodSignature);
702                 }
703
704                 internal void CheckBaked()
705                 {
706                         typeBuilder.CheckBaked();
707                 }
708
709                 internal override int GetCurrentToken()
710                 {
711                         if (typeBuilder.ModuleBuilder.IsSaved)
712                         {
713                                 return typeBuilder.ModuleBuilder.ResolvePseudoToken(pseudoToken);
714                         }
715                         else
716                         {
717                                 return pseudoToken;
718                         }
719                 }
720
721                 internal override bool IsBaked
722                 {
723                         get { return typeBuilder.IsBaked; }
724                 }
725         }
726 }