// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using SharpGen.Config;
using SharpGen.CppModel;
using SharpGen.Logging;
using SharpGen.Model;
namespace SharpGen.Transform
{
///
/// Transforms a C++ interface to a C# interface.
///
public class InterfaceTransform : TransformBase, ITransformPreparer, ITransformer
{
private readonly Dictionary _mapMoveMethodToInnerInterface = new();
private TypeRegistry TypeRegistry => Ioc.TypeRegistry;
private readonly NamespaceRegistry namespaceRegistry;
private readonly IInteropSignatureTransform interopSignatureTransform;
private readonly PropertyBuilder propertyBuilder;
private readonly MethodOverloadBuilder methodOverloadBuilder;
private readonly CsInterface DefaultCallbackable;
private readonly CsInterface CppObjectType;
public InterfaceTransform(NamingRulesManager namingRules,
ITransformPreparer methodPreparer,
ITransformer methodTransformer,
NamespaceRegistry namespaceRegistry,
IInteropSignatureTransform interopSignatureTransform,
Ioc ioc) : base(namingRules, ioc)
{
MethodPreparer = methodPreparer;
MethodTransformer = methodTransformer;
this.namespaceRegistry = namespaceRegistry;
this.interopSignatureTransform = interopSignatureTransform;
propertyBuilder = new PropertyBuilder(Ioc);
methodOverloadBuilder = new MethodOverloadBuilder(Ioc);
var globalNamespace = Ioc.GlobalNamespace;
CppObjectType = new CsInterface(null, globalNamespace.GetTypeName(WellKnownName.CppObject));
DefaultCallbackable = new CsInterface(null, globalNamespace.GetTypeName(WellKnownName.ICallbackable))
{
ShadowName = globalNamespace.GetTypeName(WellKnownName.CppObjectShadow),
VtblName = globalNamespace.GetTypeName(WellKnownName.CppObjectVtbl)
};
}
///
/// Gets the method transformer.
///
/// The method transformer.
private ITransformPreparer MethodPreparer { get; }
private ITransformer MethodTransformer { get; }
///
/// Moves the methods to an inner C# interface.
///
/// The method name regexp query.
/// The C# inner interface.
/// The name of the property to access the inner interface.
/// Name of the inherited interface.
public void MoveMethodsToInnerInterface(string methodNameRegExp, string innerInterface, string propertyNameAccess,
string inheritedInterfaceName = null)
{
_mapMoveMethodToInnerInterface.Add(new Regex("^" + methodNameRegExp + "$"),
new InnerInterfaceMethod(innerInterface, propertyNameAccess,
inheritedInterfaceName));
}
///
/// Prepares the specified C++ element to a C# element.
///
/// The C++ element.
/// The C# element created and registered to the
public override CsInterface Prepare(CppInterface cppInterface)
{
// IsFullyMapped to false => The structure is being mapped
CsInterface cSharpInterface = new(cppInterface, NamingRules.Rename(cppInterface))
{
IsFullyMapped = false
};
var nameSpace = namespaceRegistry.ResolveNamespace(cppInterface);
nameSpace.Add(cSharpInterface);
TypeRegistry.BindType(cppInterface.Name, cSharpInterface, source: cppInterface.ParentInclude?.Name);
foreach (var cppMethod in cppInterface.Methods)
{
cSharpInterface.Add(MethodPreparer.Prepare(cppMethod));
}
return cSharpInterface;
}
///
/// Processes the specified interface type.
///
/// Type of the interface.
public override void Process(CsInterface interfaceType)
{
if (interfaceType.IsFullyMapped)
return;
// Set IsFullyMapped to avoid recursive mapping
interfaceType.IsFullyMapped = true;
var cppInterface = (CppInterface)interfaceType.CppElement;
var baseType = TypeRegistry.FindBoundType(cppInterface.Base);
if (baseType != null)
{
interfaceType.Base = (CsInterface)baseType;
// Process base if it's not mapped already
if (!interfaceType.Base.IsFullyMapped)
Process(interfaceType.Base);
}
else
{
if (!interfaceType.IsCallback)
interfaceType.Base = CppObjectType;
}
// Handle Methods
var methods = interfaceType.MethodList;
List specialOverloads = new();
foreach (var cSharpMethod in methods)
{
MethodTransformer.Process(cSharpMethod);
// Add specialized method overloads
specialOverloads.AddRange(GenerateSpecialOverloads(cSharpMethod));
}
foreach (var overload in specialOverloads)
{
interfaceType.Add(overload);
MethodTransform.CreateNativeInteropSignatures(interopSignatureTransform, overload);
}
MoveMethodsToInnerInterfaces(interfaceType);
// If interfaceType is DualCallback, then need to generate a default implementation
if (interfaceType.IsDualCallback)
{
var nativeCallback = CreateNativeCallbackType(interfaceType);
interfaceType.Parent.Add(nativeCallback);
CreateProperties(nativeCallback.Methods);
}
else if (!interfaceType.IsCallback && interfaceType.Base is {IsDualCallback: true})
{
interfaceType.Base = interfaceType.Base.GetNativeImplementationOrThis();
}
// N.B. Implicit dependency on CreateNativeCallbackType having already executed.
// Otherwise Native type gets ICallbackable instead of CppObject as a base.
if (interfaceType.IsCallback)
interfaceType.Base ??= DefaultCallbackable;
CreateProperties(methods);
}
private void MoveMethodsToInnerInterfaces(CsInterface interfaceType)
{
// Dispatch method to inner interface if any
var mapInnerInterface = new Dictionary();
// Make a copy of the methods
var methods = interfaceType.Methods.ToList();
foreach (var csMethod in methods)
{
var cppName = interfaceType.CppElementName + "::" + csMethod.CppElement.Name;
foreach (var keyValuePair in _mapMoveMethodToInnerInterface)
{
if (!keyValuePair.Key.Match(cppName).Success)
continue;
var innerInterfaceName = keyValuePair.Value.InnerInterface;
var parentInterfaceName = keyValuePair.Value.InheritedInterfaceName;
CsInterface parentCsInterface = null;
if (parentInterfaceName != null)
{
if (!mapInnerInterface.TryGetValue(parentInterfaceName, out parentCsInterface))
{
parentCsInterface = new CsInterface(null, parentInterfaceName);
mapInnerInterface.Add(parentInterfaceName, parentCsInterface);
}
}
if (!mapInnerInterface.TryGetValue(innerInterfaceName, out CsInterface innerCsInterface))
{
// TODO custom cppInterface?
innerCsInterface = new CsInterface((CppInterface)interfaceType.CppElement, innerInterfaceName)
{
PropertyAccessName = keyValuePair.Value.PropertyAccessName,
Base = parentCsInterface ?? CppObjectType
};
// Add inner interface to root interface
interfaceType.Add(innerCsInterface);
interfaceType.Parent.Add(innerCsInterface);
// Move method to inner interface
mapInnerInterface.Add(innerInterfaceName, innerCsInterface);
}
interfaceType.Remove(csMethod);
innerCsInterface.Add(csMethod);
break;
}
}
}
private CsInterface CreateNativeCallbackType(CsInterface interfaceType)
{
var cppInterface = (CppInterface) interfaceType.CppElement;
var tagForInterface = cppInterface.Rule;
var nativeCallbackBase = interfaceType.Base;
var interfaceTypeName = tagForInterface.NativeCallbackName switch
{
{ } name => name,
_ => interfaceType.Name + "Native"
};
CsInterface nativeCallback = new(cppInterface, interfaceTypeName)
{
IsCallback = false,
IsDualCallback = true,
IBase = interfaceType,
Base = nativeCallbackBase switch
{
// If Parent is a DualInterface, then inherit from Default Callback
{IsDualCallback: true} baseInterface => baseInterface.GetNativeImplementationOrThis(),
_ => nativeCallbackBase ?? CppObjectType
}
};
// Update nativeCallback from tag
if (tagForInterface.NativeCallbackVisibility is {} visibility)
nativeCallback.Visibility = visibility;
interfaceType.NativeImplementation = nativeCallback;
foreach (var method in interfaceType.MethodList)
{
var newCsMethod = (CsMethod) method.Clone();
newCsMethod.Hidden = false;
MethodTransform.CreateNativeInteropSignatures(interopSignatureTransform, newCsMethod);
var keepImplementPublic = interfaceType.AutoGenerateShadow ||
method.IsPublicVisibilityForced(interfaceType);
if (!keepImplementPublic)
{
newCsMethod.Visibility = Visibility.Internal;
newCsMethod.SuffixName("_");
}
nativeCallback.Add(newCsMethod);
}
return nativeCallback;
}
///
/// Creates C# properties for the given set of methods.
///
/// The methods.
private void CreateProperties(IEnumerable methods)
{
var cSharpProperties = propertyBuilder.CreateProperties(methods);
// Add the property to the parentContainer
foreach (var property in cSharpProperties.Values)
{
propertyBuilder.AttachPropertyToParent(property);
}
}
private IEnumerable GenerateSpecialOverloads(CsMethod csMethod)
{
var hasInterfaceArrayLike = csMethod.PublicParameters.Any(param => param.IsInInterfaceArrayLike);
if (hasInterfaceArrayLike)
{
yield return methodOverloadBuilder.CreateInterfaceArrayOverload(csMethod);
}
if (hasInterfaceArrayLike || csMethod.RequestRawPtr)
{
yield return methodOverloadBuilder.CreateRawPtrOverload(csMethod);
}
}
///
/// Private class used for inner interface method creation.
///
private class InnerInterfaceMethod
{
public readonly string InnerInterface;
public readonly string PropertyAccessName;
public readonly string InheritedInterfaceName;
public InnerInterfaceMethod(string innerInterface, string propertyAccess, string inheritedInterfaceName)
{
InnerInterface = innerInterface;
PropertyAccessName = propertyAccess;
InheritedInterfaceName = inheritedInterfaceName;
}
}
}
}