// // 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 MonoFN.Cecil.Cil; using MonoFN.Collections.Generic; using System; using System.Threading; using RVA = System.UInt32; namespace MonoFN.Cecil { public sealed class MethodDefinition : MethodReference, IMemberDefinition, ISecurityDeclarationProvider, ICustomDebugInformationProvider { private ushort attributes; private ushort impl_attributes; internal volatile bool sem_attrs_ready; internal MethodSemanticsAttributes sem_attrs; private Collection custom_attributes; private Collection security_declarations; internal RVA rva; internal PInvokeInfo pinvoke; private 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 MethodDefinitionProjection WindowsRuntimeProjection { get { return (MethodDefinitionProjection)projection; } set { projection = value; } } internal void ReadSemantics() { if (sem_attrs_ready) return; ModuleDefinition module = 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 { MethodBody local = 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(this), null); return body; } set { ModuleDefinition module = 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) debug_info = null; } } } public MethodDebugInformation DebugInformation { get { Mixin.Read(Body); if (debug_info == null) { Interlocked.CompareExchange(ref debug_info, new(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(), 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(), 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 IsRuntimeSpecialName && IsSpecialName && (Name == ".cctor" || Name == ".ctor"); } } public override bool IsDefinition { get { return true; } } internal MethodDefinition() { token = new(TokenType.Method); } public MethodDefinition(string name, MethodAttributes attributes, TypeReference returnType) : base(name, returnType) { this.attributes = (ushort)attributes; HasThis = !IsStatic; token = new(TokenType.Method); } public override MethodDefinition Resolve() { return this; } } internal static partial class Mixin { public static ParameterDefinition GetParameter(this MethodBody self, int index) { MethodDefinition method = self.method; if (method.HasThis) { if (index == 0) return self.ThisParameter; index--; } Collection parameters = method.Parameters; if (index < 0 || index >= parameters.size) return null; return parameters[index]; } public static VariableDefinition GetVariable(this MethodBody self, int index) { Collection 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; } } }