mirror of https://github.com/mono/CppSharp.git
1 changed files with 272 additions and 0 deletions
@ -0,0 +1,272 @@
@@ -0,0 +1,272 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using CppSharp.AST; |
||||
using CppSharp.Generators; |
||||
|
||||
namespace CppSharp.Passes |
||||
{ |
||||
public class ExtractInterfacePass : TranslationUnitPass |
||||
{ |
||||
/// <summary>
|
||||
/// Collects all interfaces in a unit to be added at the end
|
||||
/// because the unit cannot be changed while it's being iterated though.
|
||||
/// We also need it to check if a class already has a complementary interface
|
||||
/// because different classes may have the same secondary bases.
|
||||
/// </summary>
|
||||
private readonly HashSet<Class> interfaces = new HashSet<Class>(); |
||||
|
||||
/// <summary>
|
||||
/// Change and implement secondary bases at the end to avoid processing implementations.
|
||||
/// </summary>
|
||||
private readonly HashSet<Class> classesNeedingInterface = new HashSet<Class>(); |
||||
|
||||
public ExtractInterfacePass() |
||||
=> VisitOptions.ResetFlags(VisitFlags.Default); |
||||
|
||||
public override bool VisitASTContext(ASTContext context) |
||||
{ |
||||
bool result = base.VisitASTContext(context); |
||||
foreach (var @interface in interfaces.Where(i => !(i is ClassTemplateSpecialization))) |
||||
{ |
||||
int index = @interface.Namespace.Declarations.IndexOf(@interface.OriginalClass); |
||||
@interface.Namespace.Declarations.Insert(index, @interface); |
||||
} |
||||
|
||||
foreach (Class @class in classesNeedingInterface) |
||||
{ |
||||
Class @interface = interfaces.FirstOrDefault(iface => iface.OriginalClass == @class); |
||||
ImplementInterfaceMethods(@class, @interface); |
||||
ImplementInterfaceProperties(@class, @interface); |
||||
} |
||||
|
||||
interfaces.Clear(); |
||||
classesNeedingInterface.Clear(); |
||||
return result; |
||||
} |
||||
|
||||
public override bool VisitClassDecl(Class @class) |
||||
{ |
||||
if (!@class.IsGenerated) |
||||
return false; |
||||
|
||||
if (@class.IsInterface) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
classesNeedingInterface.Add(@class); |
||||
Console.WriteLine(@class.Name); |
||||
GetInterface(@class); |
||||
return true; |
||||
} |
||||
|
||||
private Class GetInterface(Class @base) |
||||
{ |
||||
if (@base.CompleteDeclaration != null) |
||||
@base = (Class) @base.CompleteDeclaration; |
||||
|
||||
return interfaces.FirstOrDefault(i => i.OriginalClass == @base) ?? |
||||
GetNewInterface("I" + @base.Name, @base); |
||||
} |
||||
|
||||
private Class GetNewInterface(string name, Class @base) |
||||
{ |
||||
var specialization = @base as ClassTemplateSpecialization; |
||||
Class @interface; |
||||
if (specialization == null) |
||||
{ |
||||
@interface = new Class(); |
||||
} |
||||
else |
||||
{ |
||||
Class template = specialization.TemplatedDecl.TemplatedClass; |
||||
Class templatedInterface = GetInterface(template); |
||||
@interface = interfaces.FirstOrDefault(i => i.OriginalClass == @base); |
||||
if (@interface != null) |
||||
return @interface; |
||||
var specializedInterface = new ClassTemplateSpecialization(); |
||||
specializedInterface.Arguments.AddRange(specialization.Arguments); |
||||
specializedInterface.TemplatedDecl = new ClassTemplate { TemplatedDecl = templatedInterface }; |
||||
@interface = specializedInterface; |
||||
} |
||||
@interface.Name = name; |
||||
@interface.USR = @base.USR; |
||||
@interface.Namespace = @base.Namespace; |
||||
@interface.Access = @base.Access; |
||||
@interface.Type = ClassType.Interface; |
||||
@interface.OriginalClass = @base; |
||||
|
||||
@interface.Bases.AddRange( |
||||
from b in @base.Bases |
||||
where b.Class != null |
||||
let i = GetInterface(b.Class) |
||||
select new BaseClassSpecifier(b) { Type = new TagType(i) }); |
||||
|
||||
@interface.Methods.AddRange( |
||||
from m in @base.Methods |
||||
where !m.IsConstructor && !m.IsDestructor && !m.IsStatic && |
||||
(m.IsGenerated || (m.IsInvalid && specialization != null)) && !m.IsOperator |
||||
select new Method(m) { Namespace = @interface, OriginalFunction = m }); |
||||
|
||||
@interface.Properties.AddRange( |
||||
from property in @base.Properties |
||||
where property.IsDeclared |
||||
select CreateInterfaceProperty(property, @interface)); |
||||
|
||||
@interface.Fields.AddRange(@base.Fields); |
||||
// avoid conflicts when potentially renaming later
|
||||
@interface.Declarations.AddRange(@base.Declarations); |
||||
|
||||
if (@interface.Bases.Count == 0) |
||||
{ |
||||
QualifiedType intPtr = new QualifiedType( |
||||
new BuiltinType(PrimitiveType.IntPtr)); |
||||
|
||||
var instance = new Property |
||||
{ |
||||
Namespace = @interface, |
||||
Name = Helpers.InstanceIdentifier, |
||||
QualifiedType = intPtr, |
||||
GetMethod = new Method |
||||
{ |
||||
Name = Helpers.InstanceIdentifier, |
||||
SynthKind = FunctionSynthKind.InterfaceInstance, |
||||
Namespace = @interface, |
||||
OriginalReturnType = intPtr |
||||
} |
||||
}; |
||||
|
||||
@interface.Properties.Add(instance); |
||||
|
||||
var dispose = new Method |
||||
{ |
||||
Namespace = @interface, |
||||
Name = "Dispose", |
||||
ReturnType = new QualifiedType(new BuiltinType(PrimitiveType.Void)), |
||||
SynthKind = FunctionSynthKind.InterfaceDispose, |
||||
Mangled = string.Empty |
||||
}; |
||||
|
||||
@interface.Methods.Add(dispose); |
||||
} |
||||
|
||||
@interface.Declarations.AddRange(@base.Events); |
||||
|
||||
var type = new QualifiedType(new BuiltinType(PrimitiveType.IntPtr)); |
||||
string pointerAdjustment = "__PointerTo" + @base.Name; |
||||
var adjustmentTo = new Property |
||||
{ |
||||
Namespace = @interface, |
||||
Name = pointerAdjustment, |
||||
QualifiedType = type, |
||||
GetMethod = new Method |
||||
{ |
||||
Name = pointerAdjustment, |
||||
SynthKind = FunctionSynthKind.InterfaceInstance, |
||||
Namespace = @interface, |
||||
ReturnType = type |
||||
} |
||||
}; |
||||
@interface.Properties.Add(adjustmentTo); |
||||
@base.Properties.Add(adjustmentTo); |
||||
|
||||
@base.Bases.Add(new BaseClassSpecifier { Type = new TagType(@interface) }); |
||||
|
||||
interfaces.Add(@interface); |
||||
if (@base.IsTemplate) |
||||
{ |
||||
@interface.IsDependent = true; |
||||
@interface.TemplateParameters.AddRange(@base.TemplateParameters); |
||||
templatedInterfaces[@base] = @interface; |
||||
foreach (var spec in @base.Specializations) |
||||
@interface.Specializations.Add( |
||||
(ClassTemplateSpecialization) GetNewInterface(name, spec)); |
||||
} |
||||
return @interface; |
||||
} |
||||
|
||||
private static Property CreateInterfaceProperty(Property property, DeclarationContext @namespace) |
||||
{ |
||||
var interfaceProperty = new Property(property) { Namespace = @namespace }; |
||||
if (property.GetMethod != null) |
||||
{ |
||||
interfaceProperty.GetMethod = new Method(property.GetMethod) |
||||
{ |
||||
OriginalFunction = property.GetMethod, |
||||
Namespace = @namespace |
||||
}; |
||||
} |
||||
|
||||
if (property.SetMethod != null) |
||||
{ |
||||
// handle indexers
|
||||
interfaceProperty.SetMethod = property.GetMethod == property.SetMethod ? |
||||
interfaceProperty.GetMethod : new Method(property.SetMethod) |
||||
{ |
||||
OriginalFunction = property.SetMethod, |
||||
Namespace = @namespace |
||||
}; |
||||
} |
||||
|
||||
return interfaceProperty; |
||||
} |
||||
|
||||
private void ImplementInterfaceMethods(Class @class, Class @interface) |
||||
{ |
||||
foreach (var method in @interface.Methods.Where( |
||||
m => m.SynthKind != FunctionSynthKind.InterfaceDispose)) |
||||
{ |
||||
var existingImpl = @class.Methods.Find( |
||||
m => m.OriginalName == method.OriginalName && |
||||
m.Parameters.Where(p => !p.Ignore).SequenceEqual( |
||||
method.Parameters.Where(p => !p.Ignore), |
||||
ParameterTypeComparer.Instance)); |
||||
|
||||
if (existingImpl != null) |
||||
{ |
||||
if (existingImpl.OriginalFunction == null) |
||||
existingImpl.OriginalFunction = method; |
||||
|
||||
continue; |
||||
} |
||||
|
||||
var impl = new Method(method) |
||||
{ |
||||
Namespace = @class, |
||||
OriginalNamespace = @interface, |
||||
OriginalFunction = method.OriginalFunction |
||||
}; |
||||
|
||||
var rootBaseMethod = @class.GetBaseMethod(method); |
||||
if (rootBaseMethod != null && rootBaseMethod.IsDeclared) |
||||
impl.ExplicitInterfaceImpl = @interface; |
||||
|
||||
@class.Methods.Add(impl); |
||||
} |
||||
|
||||
foreach (var @base in @interface.Bases) |
||||
ImplementInterfaceMethods(@class, @base.Class); |
||||
} |
||||
|
||||
private static void ImplementInterfaceProperties(Class @class, Class @interface) |
||||
{ |
||||
foreach (var property in @interface.Properties.Where(p => p.Name != Helpers.InstanceIdentifier)) |
||||
{ |
||||
var impl = CreateInterfaceProperty(property, @class); |
||||
impl.OriginalNamespace = @interface; |
||||
|
||||
var rootBaseProperty = @class.GetBasePropertyByName(property, true); |
||||
if (rootBaseProperty != null && rootBaseProperty.IsDeclared) |
||||
impl.ExplicitInterfaceImpl = @interface; |
||||
|
||||
@class.Properties.Add(impl); |
||||
} |
||||
|
||||
foreach (var @base in @interface.Bases) |
||||
ImplementInterfaceProperties(@class, @base.Class); |
||||
} |
||||
|
||||
private readonly Dictionary<Class, Class> templatedInterfaces = new Dictionary<Class, Class>(); |
||||
} |
||||
} |
Loading…
Reference in new issue