// // Author: // Jb Evain (jbevain@gmail.com) // // Copyright (c) 2008 - 2015 Jb Evain // Copyright (c) 2008 - 2011 Novell, Inc. // // Licensed under the MIT/X11 license. // using System; using System.Threading; using Mono.Cecil.Cil; using Mono.Collections.Generic; using RVA = System.UInt32; namespace Mono.Cecil { public sealed class MethodDefinition : MethodReference, IMemberDefinition, ISecurityDeclarationProvider, ICustomDebugInformationProvider { ushort attributes; ushort impl_attributes; internal volatile bool sem_attrs_ready; internal MethodSemanticsAttributes sem_attrs; Collection custom_attributes; Collection security_declarations; internal RVA rva; internal PInvokeInfo pinvoke; Collection overrides; internal MethodBody body; internal MethodDebugInformation debug_info; internal Collection custom_infos; public override string Name { get { return base.Name; } set { if (IsWindowsRuntimeProjection && value != base.Name) throw new InvalidOperationException (); base.Name = value; } } public MethodAttributes Attributes { get { return (MethodAttributes) attributes; } set { if (IsWindowsRuntimeProjection && (ushort) value != attributes) throw new InvalidOperationException (); attributes = (ushort) value; } } public MethodImplAttributes ImplAttributes { get { return (MethodImplAttributes) impl_attributes; } set { if (IsWindowsRuntimeProjection && (ushort) value != impl_attributes) throw new InvalidOperationException (); impl_attributes = (ushort) value; } } public MethodSemanticsAttributes SemanticsAttributes { get { if (sem_attrs_ready) return sem_attrs; if (HasImage) { ReadSemantics (); return sem_attrs; } sem_attrs = MethodSemanticsAttributes.None; sem_attrs_ready = true; return sem_attrs; } set { sem_attrs = value; } } internal new MethodDefinitionProjection WindowsRuntimeProjection { get { return (MethodDefinitionProjection) projection; } set { projection = value; } } internal void ReadSemantics () { if (sem_attrs_ready) return; var module = this.Module; if (module == null) return; if (!module.HasImage) return; lock (module.SyncRoot) { if (sem_attrs_ready) return; module.Read (this, (method, reader) => reader.ReadAllSemantics (method)); } } public bool HasSecurityDeclarations { get { if (security_declarations != null) return security_declarations.Count > 0; return this.GetHasSecurityDeclarations (Module); } } public Collection SecurityDeclarations { get { return security_declarations ?? (this.GetSecurityDeclarations (ref security_declarations, Module)); } } public bool HasCustomAttributes { get { if (custom_attributes != null) return custom_attributes.Count > 0; return this.GetHasCustomAttributes (Module); } } public Collection CustomAttributes { get { return custom_attributes ?? (this.GetCustomAttributes (ref custom_attributes, Module)); } } public int RVA { get { return (int) rva; } } public bool HasBody { get { return (attributes & (ushort) MethodAttributes.Abstract) == 0 && (attributes & (ushort) MethodAttributes.PInvokeImpl) == 0 && (impl_attributes & (ushort) MethodImplAttributes.InternalCall) == 0 && (impl_attributes & (ushort) MethodImplAttributes.Native) == 0 && (impl_attributes & (ushort) MethodImplAttributes.Unmanaged) == 0 && (impl_attributes & (ushort) MethodImplAttributes.Runtime) == 0; } } public MethodBody Body { get { var local = this.body; if (local != null) return local; if (!HasBody) return null; if (HasImage && rva != 0) return Module.Read (ref body, this, (method, reader) => reader.ReadMethodBody (method)); Interlocked.CompareExchange (ref body, new MethodBody (this) , null); return body; } set { var module = this.Module; if (module == null) { body = value; return; } // we reset Body to null in ILSpy to save memory; so we need that operation to be thread-safe lock (module.SyncRoot) { body = value; if (value == null) this.debug_info = null; } } } public MethodDebugInformation DebugInformation { get { Mixin.Read (Body); if (debug_info == null) { Interlocked.CompareExchange (ref debug_info, new MethodDebugInformation (this), null); } return debug_info; } set { debug_info = value; } } public bool HasPInvokeInfo { get { if (pinvoke != null) return true; return IsPInvokeImpl; } } public PInvokeInfo PInvokeInfo { get { if (pinvoke != null) return pinvoke; if (HasImage && IsPInvokeImpl) return Module.Read (ref pinvoke, this, (method, reader) => reader.ReadPInvokeInfo (method)); return null; } set { IsPInvokeImpl = true; pinvoke = value; } } public bool HasOverrides { get { if (overrides != null) return overrides.Count > 0; return HasImage && Module.Read (this, (method, reader) => reader.HasOverrides (method)); } } public Collection Overrides { get { if (overrides != null) return overrides; if (HasImage) return Module.Read (ref overrides, this, (method, reader) => reader.ReadOverrides (method)); Interlocked.CompareExchange (ref overrides, new Collection (), null); return overrides; } } public override bool HasGenericParameters { get { if (generic_parameters != null) return generic_parameters.Count > 0; return this.GetHasGenericParameters (Module); } } public override Collection GenericParameters { get { return generic_parameters ?? (this.GetGenericParameters (ref generic_parameters, Module)); } } public bool HasCustomDebugInformations { get { Mixin.Read (Body); return !custom_infos.IsNullOrEmpty (); } } public Collection CustomDebugInformations { get { Mixin.Read (Body); if (custom_infos == null) Interlocked.CompareExchange (ref custom_infos, new Collection (), null); return custom_infos; } } #region MethodAttributes public bool IsCompilerControlled { get { return attributes.GetMaskedAttributes ((ushort) MethodAttributes.MemberAccessMask, (ushort) MethodAttributes.CompilerControlled); } set { attributes = attributes.SetMaskedAttributes ((ushort) MethodAttributes.MemberAccessMask, (ushort) MethodAttributes.CompilerControlled, value); } } public bool IsPrivate { get { return attributes.GetMaskedAttributes ((ushort) MethodAttributes.MemberAccessMask, (ushort) MethodAttributes.Private); } set { attributes = attributes.SetMaskedAttributes ((ushort) MethodAttributes.MemberAccessMask, (ushort) MethodAttributes.Private, value); } } public bool IsFamilyAndAssembly { get { return attributes.GetMaskedAttributes ((ushort) MethodAttributes.MemberAccessMask, (ushort) MethodAttributes.FamANDAssem); } set { attributes = attributes.SetMaskedAttributes ((ushort) MethodAttributes.MemberAccessMask, (ushort) MethodAttributes.FamANDAssem, value); } } public bool IsAssembly { get { return attributes.GetMaskedAttributes ((ushort) MethodAttributes.MemberAccessMask, (ushort) MethodAttributes.Assembly); } set { attributes = attributes.SetMaskedAttributes ((ushort) MethodAttributes.MemberAccessMask, (ushort) MethodAttributes.Assembly, value); } } public bool IsFamily { get { return attributes.GetMaskedAttributes ((ushort) MethodAttributes.MemberAccessMask, (ushort) MethodAttributes.Family); } set { attributes = attributes.SetMaskedAttributes ((ushort) MethodAttributes.MemberAccessMask, (ushort) MethodAttributes.Family, value); } } public bool IsFamilyOrAssembly { get { return attributes.GetMaskedAttributes ((ushort) MethodAttributes.MemberAccessMask, (ushort) MethodAttributes.FamORAssem); } set { attributes = attributes.SetMaskedAttributes ((ushort) MethodAttributes.MemberAccessMask, (ushort) MethodAttributes.FamORAssem, value); } } public bool IsPublic { get { return attributes.GetMaskedAttributes ((ushort) MethodAttributes.MemberAccessMask, (ushort) MethodAttributes.Public); } set { attributes = attributes.SetMaskedAttributes ((ushort) MethodAttributes.MemberAccessMask, (ushort) MethodAttributes.Public, value); } } public bool IsStatic { get { return attributes.GetAttributes ((ushort) MethodAttributes.Static); } set { attributes = attributes.SetAttributes ((ushort) MethodAttributes.Static, value); } } public bool IsFinal { get { return attributes.GetAttributes ((ushort) MethodAttributes.Final); } set { attributes = attributes.SetAttributes ((ushort) MethodAttributes.Final, value); } } public bool IsVirtual { get { return attributes.GetAttributes ((ushort) MethodAttributes.Virtual); } set { attributes = attributes.SetAttributes ((ushort) MethodAttributes.Virtual, value); } } public bool IsHideBySig { get { return attributes.GetAttributes ((ushort) MethodAttributes.HideBySig); } set { attributes = attributes.SetAttributes ((ushort) MethodAttributes.HideBySig, value); } } public bool IsReuseSlot { get { return attributes.GetMaskedAttributes ((ushort) MethodAttributes.VtableLayoutMask, (ushort) MethodAttributes.ReuseSlot); } set { attributes = attributes.SetMaskedAttributes ((ushort) MethodAttributes.VtableLayoutMask, (ushort) MethodAttributes.ReuseSlot, value); } } public bool IsNewSlot { get { return attributes.GetMaskedAttributes ((ushort) MethodAttributes.VtableLayoutMask, (ushort) MethodAttributes.NewSlot); } set { attributes = attributes.SetMaskedAttributes ((ushort) MethodAttributes.VtableLayoutMask, (ushort) MethodAttributes.NewSlot, value); } } public bool IsCheckAccessOnOverride { get { return attributes.GetAttributes ((ushort) MethodAttributes.CheckAccessOnOverride); } set { attributes = attributes.SetAttributes ((ushort) MethodAttributes.CheckAccessOnOverride, value); } } public bool IsAbstract { get { return attributes.GetAttributes ((ushort) MethodAttributes.Abstract); } set { attributes = attributes.SetAttributes ((ushort) MethodAttributes.Abstract, value); } } public bool IsSpecialName { get { return attributes.GetAttributes ((ushort) MethodAttributes.SpecialName); } set { attributes = attributes.SetAttributes ((ushort) MethodAttributes.SpecialName, value); } } public bool IsPInvokeImpl { get { return attributes.GetAttributes ((ushort) MethodAttributes.PInvokeImpl); } set { attributes = attributes.SetAttributes ((ushort) MethodAttributes.PInvokeImpl, value); } } public bool IsUnmanagedExport { get { return attributes.GetAttributes ((ushort) MethodAttributes.UnmanagedExport); } set { attributes = attributes.SetAttributes ((ushort) MethodAttributes.UnmanagedExport, value); } } public bool IsRuntimeSpecialName { get { return attributes.GetAttributes ((ushort) MethodAttributes.RTSpecialName); } set { attributes = attributes.SetAttributes ((ushort) MethodAttributes.RTSpecialName, value); } } public bool HasSecurity { get { return attributes.GetAttributes ((ushort) MethodAttributes.HasSecurity); } set { attributes = attributes.SetAttributes ((ushort) MethodAttributes.HasSecurity, value); } } #endregion #region MethodImplAttributes public bool IsIL { get { return impl_attributes.GetMaskedAttributes ((ushort) MethodImplAttributes.CodeTypeMask, (ushort) MethodImplAttributes.IL); } set { impl_attributes = impl_attributes.SetMaskedAttributes ((ushort) MethodImplAttributes.CodeTypeMask, (ushort) MethodImplAttributes.IL, value); } } public bool IsNative { get { return impl_attributes.GetMaskedAttributes ((ushort) MethodImplAttributes.CodeTypeMask, (ushort) MethodImplAttributes.Native); } set { impl_attributes = impl_attributes.SetMaskedAttributes ((ushort) MethodImplAttributes.CodeTypeMask, (ushort) MethodImplAttributes.Native, value); } } public bool IsRuntime { get { return impl_attributes.GetMaskedAttributes ((ushort) MethodImplAttributes.CodeTypeMask, (ushort) MethodImplAttributes.Runtime); } set { impl_attributes = impl_attributes.SetMaskedAttributes ((ushort) MethodImplAttributes.CodeTypeMask, (ushort) MethodImplAttributes.Runtime, value); } } public bool IsUnmanaged { get { return impl_attributes.GetMaskedAttributes ((ushort) MethodImplAttributes.ManagedMask, (ushort) MethodImplAttributes.Unmanaged); } set { impl_attributes = impl_attributes.SetMaskedAttributes ((ushort) MethodImplAttributes.ManagedMask, (ushort) MethodImplAttributes.Unmanaged, value); } } public bool IsManaged { get { return impl_attributes.GetMaskedAttributes ((ushort) MethodImplAttributes.ManagedMask, (ushort) MethodImplAttributes.Managed); } set { impl_attributes = impl_attributes.SetMaskedAttributes ((ushort) MethodImplAttributes.ManagedMask, (ushort) MethodImplAttributes.Managed, value); } } public bool IsForwardRef { get { return impl_attributes.GetAttributes ((ushort) MethodImplAttributes.ForwardRef); } set { impl_attributes = impl_attributes.SetAttributes ((ushort) MethodImplAttributes.ForwardRef, value); } } public bool IsPreserveSig { get { return impl_attributes.GetAttributes ((ushort) MethodImplAttributes.PreserveSig); } set { impl_attributes = impl_attributes.SetAttributes ((ushort) MethodImplAttributes.PreserveSig, value); } } public bool IsInternalCall { get { return impl_attributes.GetAttributes ((ushort) MethodImplAttributes.InternalCall); } set { impl_attributes = impl_attributes.SetAttributes ((ushort) MethodImplAttributes.InternalCall, value); } } public bool IsSynchronized { get { return impl_attributes.GetAttributes ((ushort) MethodImplAttributes.Synchronized); } set { impl_attributes = impl_attributes.SetAttributes ((ushort) MethodImplAttributes.Synchronized, value); } } public bool NoInlining { get { return impl_attributes.GetAttributes ((ushort) MethodImplAttributes.NoInlining); } set { impl_attributes = impl_attributes.SetAttributes ((ushort) MethodImplAttributes.NoInlining, value); } } public bool NoOptimization { get { return impl_attributes.GetAttributes ((ushort) MethodImplAttributes.NoOptimization); } set { impl_attributes = impl_attributes.SetAttributes ((ushort) MethodImplAttributes.NoOptimization, value); } } public bool AggressiveInlining { get { return impl_attributes.GetAttributes ((ushort) MethodImplAttributes.AggressiveInlining); } set { impl_attributes = impl_attributes.SetAttributes ((ushort) MethodImplAttributes.AggressiveInlining, value); } } #endregion #region MethodSemanticsAttributes public bool IsSetter { get { return this.GetSemantics (MethodSemanticsAttributes.Setter); } set { this.SetSemantics (MethodSemanticsAttributes.Setter, value); } } public bool IsGetter { get { return this.GetSemantics (MethodSemanticsAttributes.Getter); } set { this.SetSemantics (MethodSemanticsAttributes.Getter, value); } } public bool IsOther { get { return this.GetSemantics (MethodSemanticsAttributes.Other); } set { this.SetSemantics (MethodSemanticsAttributes.Other, value); } } public bool IsAddOn { get { return this.GetSemantics (MethodSemanticsAttributes.AddOn); } set { this.SetSemantics (MethodSemanticsAttributes.AddOn, value); } } public bool IsRemoveOn { get { return this.GetSemantics (MethodSemanticsAttributes.RemoveOn); } set { this.SetSemantics (MethodSemanticsAttributes.RemoveOn, value); } } public bool IsFire { get { return this.GetSemantics (MethodSemanticsAttributes.Fire); } set { this.SetSemantics (MethodSemanticsAttributes.Fire, value); } } #endregion public new TypeDefinition DeclaringType { get { return (TypeDefinition) base.DeclaringType; } set { base.DeclaringType = value; } } public bool IsConstructor { get { return this.IsRuntimeSpecialName && this.IsSpecialName && (this.Name == ".cctor" || this.Name == ".ctor"); } } public override bool IsDefinition { get { return true; } } internal MethodDefinition () { this.token = new MetadataToken (TokenType.Method); } public MethodDefinition (string name, MethodAttributes attributes, TypeReference returnType) : base (name, returnType) { this.attributes = (ushort) attributes; this.HasThis = !this.IsStatic; this.token = new MetadataToken (TokenType.Method); } public override MethodDefinition Resolve () { return this; } } static partial class Mixin { public static ParameterDefinition GetParameter (this MethodBody self, int index) { var method = self.method; if (method.HasThis) { if (index == 0) return self.ThisParameter; index--; } var parameters = method.Parameters; if (index < 0 || index >= parameters.size) return null; return parameters [index]; } public static VariableDefinition GetVariable (this MethodBody self, int index) { var variables = self.Variables; if (index < 0 || index >= variables.size) return null; return variables [index]; } public static bool GetSemantics (this MethodDefinition self, MethodSemanticsAttributes semantics) { return (self.SemanticsAttributes & semantics) != 0; } public static void SetSemantics (this MethodDefinition self, MethodSemanticsAttributes semantics, bool value) { if (value) self.SemanticsAttributes |= semantics; else self.SemanticsAttributes &= ~semantics; } } }