using System; using System.Collections.Generic; using System.Linq; using SharpGen.CppModel; using SharpGen.Transform; namespace SharpGen.Model { public abstract class CsCallable : CsBase, IExpiring { private CsBaseItemListCache _parameters; private Dictionary interopSignatures; protected readonly Ioc Ioc; protected abstract int MaxSizeReturnParameter { get; } protected CsCallable(Ioc ioc, CppCallable callable, string name) : base(callable, name) { Ioc = ioc ?? throw new ArgumentNullException(nameof(ioc)); if (callable == null) return; var tag = callable.Rule; CheckReturnType = tag.MethodCheckReturnType ?? CheckReturnType; ForceReturnType = tag.ParameterUsedAsReturnType ?? ForceReturnType; AlwaysReturnHResult = tag.AlwaysReturnHResult ?? AlwaysReturnHResult; RequestRawPtr = tag.RawPtr ?? RequestRawPtr; CppSignature = callable.ToString(); ShortName = callable.ToShortString(); CppCallingConvention = callable.CallingConvention; } public CppCallingConvention CppCallingConvention { get; } = CppCallingConvention.Unknown; public bool RequestRawPtr { get; } private string CppSignature { get; } private string ShortName { get; } public bool CheckReturnType { get; } public bool ForceReturnType { get; } private bool AlwaysReturnHResult { get; } public CsReturnValue ReturnValue { get; set; } public bool SignatureOnly => GetParent()?.IsCallback ?? false; public override string DocUnmanagedName => CppSignature ?? "Unknown"; public override string DocUnmanagedShortName => ShortName; public IReadOnlyList Parameters => _parameters.GetList(this); public IEnumerable PublicParameters => _parameters.Enumerate(this) .Where(param => !param.UsedAsReturn && param.Relations.Count == 0); public IEnumerable InRefInRefParameters => _parameters.Enumerate(this) .Where(param => !param.IsOut); public IEnumerable LocalManagedReferenceParameters => _parameters.Enumerate(this) .Where(param => param.IsLocalManagedReference); public CsMarshalCallableBase ActualReturnValue { get { foreach (var param in _parameters.Enumerate(this).Where(param => param.UsedAsReturn)) { return param; } return ReturnValue; } } public override void FillDocItems(IList docItems, IDocumentationLinker manager) { foreach (var param in PublicParameters) docItems.Add("" + manager.GetSingleDoc(param) + ""); if (HasReturnType) docItems.Add("" + manager.GetSingleDoc(ActualReturnValue) + ""); } public bool IsReturnStructLarge { get { // Workaround for https://github.com/dotnet/coreclr/issues/19474. This workaround is sufficient // for DirectX on Windows x86 and x64. It may produce incorrect code on other platforms depending // on the calling convention details. if (ReturnValue.MarshalType is CsStruct csStruct) { return !csStruct.IsNativePrimitive && csStruct.Size > MaxSizeReturnParameter; } return false; } } public Dictionary InteropSignatures { get => interopSignatures ?? throw new InvalidOperationException($"Accessing non-initialized {nameof(InteropSignatures)}"); set { if (interopSignatures != null) throw new InvalidOperationException($"Setting initialized {nameof(InteropSignatures)}"); interopSignatures = value; } } // Hide return type only if it is a HRESULT and AlwaysReturnHResult is false public bool IsReturnTypeHidden => CheckReturnType && IsReturnTypeResult && !AlwaysReturnHResult; public bool IsReturnTypeResult => ReturnValue.PublicType.IsWellKnownType(Ioc.GlobalNamespace, WellKnownName.Result); public bool HasReturnType => ReturnValue.PublicType != TypeRegistry.Void; internal bool HasReturnStatement => HasReturnTypeParameter || HasReturnTypeValue; internal bool HasReturnTypeValue => HasReturnType && (ForceReturnType || !IsReturnTypeHidden); /// /// Returns true if a parameter is marked to be used as the return type. /// public bool HasReturnTypeParameter => _parameters.Enumerate(this).Any(param => param.UsedAsReturn); /// /// Return the Public return type. If a out parameter is used as a public return type /// then use the type of the out parameter for the public API. /// public string PublicReturnTypeQualifiedName { get { var returnValue = ActualReturnValue; if (returnValue is CsParameter) return returnValue.PublicType.QualifiedName; if (IsReturnTypeHidden && !ForceReturnType) return "void"; return returnValue.PublicType.QualifiedName; } } /// /// Return the name of the variable used to return the value /// public string ReturnName => ActualReturnValue.Name; private protected override IEnumerable ExpiringOnItemsChange { get { yield return _parameters.Expiring; yield return this; } } public virtual CsCallable Clone() { var method = (CsCallable) MemberwiseClone(); // Clear cached parameters method.Expire(); method.ResetItems(); foreach (var parameter in Parameters) method.Add(parameter.Clone()); method.ResetParentAfterClone(); return method; } public void Expire() { interopSignatures = null; } public override IEnumerable AdditionalItems => AppendNonNull(base.AdditionalItems, ReturnValue); } }