|
|
@ -1,5 +1,8 @@ |
|
|
|
using System; |
|
|
|
using System; |
|
|
|
|
|
|
|
using System.Collections.Generic; |
|
|
|
|
|
|
|
using System.IO; |
|
|
|
using System.Linq; |
|
|
|
using System.Linq; |
|
|
|
|
|
|
|
using System.Reflection; |
|
|
|
using CppSharp.AST; |
|
|
|
using CppSharp.AST; |
|
|
|
|
|
|
|
|
|
|
|
namespace CppSharp.Passes |
|
|
|
namespace CppSharp.Passes |
|
|
@ -10,6 +13,12 @@ namespace CppSharp.Passes |
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
public class GetterSetterToPropertyPass : TranslationUnitPass |
|
|
|
public class GetterSetterToPropertyPass : TranslationUnitPass |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
private readonly List<Method> setters = new List<Method>(); |
|
|
|
|
|
|
|
private readonly List<Method> setMethods = new List<Method>(); |
|
|
|
|
|
|
|
private readonly List<Method> nonSetters = new List<Method>(); |
|
|
|
|
|
|
|
private readonly HashSet<Method> getters = new HashSet<Method>(); |
|
|
|
|
|
|
|
private readonly HashSet<string> verbs = new HashSet<string>(); |
|
|
|
|
|
|
|
|
|
|
|
public GetterSetterToPropertyPass() |
|
|
|
public GetterSetterToPropertyPass() |
|
|
|
{ |
|
|
|
{ |
|
|
|
Options.VisitClassFields = false; |
|
|
|
Options.VisitClassFields = false; |
|
|
@ -21,123 +30,169 @@ namespace CppSharp.Passes |
|
|
|
Options.VisitNamespaceVariables = false; |
|
|
|
Options.VisitNamespaceVariables = false; |
|
|
|
Options.VisitFunctionParameters = false; |
|
|
|
Options.VisitFunctionParameters = false; |
|
|
|
Options.VisitTemplateArguments = false; |
|
|
|
Options.VisitTemplateArguments = false; |
|
|
|
|
|
|
|
using (var resourceStream = Assembly.GetExecutingAssembly() |
|
|
|
|
|
|
|
.GetManifestResourceStream("CppSharp.Generator.Passes.verbs.txt")) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
using (StreamReader streamReader = new StreamReader(resourceStream)) |
|
|
|
|
|
|
|
while (!streamReader.EndOfStream) |
|
|
|
|
|
|
|
verbs.Add(streamReader.ReadLine()); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static bool IsSetter(Function method) |
|
|
|
public override bool VisitTranslationUnit(TranslationUnit unit) |
|
|
|
{ |
|
|
|
{ |
|
|
|
var isRetVoid = method.ReturnType.Type.IsPrimitiveType( |
|
|
|
bool result = base.VisitTranslationUnit(unit); |
|
|
|
PrimitiveType.Void); |
|
|
|
GenerateProperties(); |
|
|
|
|
|
|
|
return result; |
|
|
|
var isSetter = method.OriginalName.StartsWith("set", |
|
|
|
|
|
|
|
StringComparison.InvariantCultureIgnoreCase); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return isRetVoid && isSetter && method.Parameters.Count == 1; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static bool IsGetter(Function method) |
|
|
|
public override bool VisitMethodDecl(Method method) |
|
|
|
{ |
|
|
|
{ |
|
|
|
var isRetVoid = method.ReturnType.Type.IsPrimitiveType( |
|
|
|
if (!method.IsConstructor && !method.IsDestructor && !method.IsOperator && |
|
|
|
PrimitiveType.Void); |
|
|
|
!method.Ignore) |
|
|
|
|
|
|
|
DistributeMethod(method); |
|
|
|
var isGetter = method.OriginalName.StartsWith("get", |
|
|
|
return base.VisitMethodDecl(method); |
|
|
|
StringComparison.InvariantCultureIgnoreCase); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return !isRetVoid && isGetter && method.Parameters.Count == 0; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Property GetOrCreateProperty(Class @class, string name, QualifiedType type) |
|
|
|
public void GenerateProperties() |
|
|
|
{ |
|
|
|
{ |
|
|
|
var prop = @class.Properties.FirstOrDefault(property => property.Name == name |
|
|
|
GenerateProperties(setters, false); |
|
|
|
&& property.QualifiedType.Equals(type)); |
|
|
|
GenerateProperties(setMethods, true); |
|
|
|
|
|
|
|
|
|
|
|
var prop2 = @class.Properties.FirstOrDefault(property => property.Name == name); |
|
|
|
foreach (Method getter in |
|
|
|
|
|
|
|
from getter in getters |
|
|
|
if (prop == null && prop2 != null) |
|
|
|
where getter.IsGenerated && |
|
|
|
Driver.Diagnostics.EmitWarning(DiagnosticId.PropertySynthetized, |
|
|
|
((Class) getter.Namespace).Methods.All(m => m == getter || m.Name != getter.Name) |
|
|
|
"Property {0}::{1} already exist with type {2}", @class.Name, name, type.Type.ToString()); |
|
|
|
select getter) |
|
|
|
|
|
|
|
|
|
|
|
if (prop != null) |
|
|
|
|
|
|
|
return prop; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
prop = new Property |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
Name = name, |
|
|
|
// Make it a read-only property
|
|
|
|
Namespace = @class, |
|
|
|
GenerateProperty(getter.Namespace, getter); |
|
|
|
QualifiedType = type |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@class.Properties.Add(prop); |
|
|
|
|
|
|
|
return prop; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public override bool VisitMethodDecl(Method method) |
|
|
|
private void GenerateProperties(IEnumerable<Method> settersToUse, bool readOnly) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (AlreadyVisited(method)) |
|
|
|
foreach (var group in settersToUse.GroupBy(m => m.Namespace)) |
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (ASTUtils.CheckIgnoreMethod(method)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var @class = method.Namespace as Class; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (@class == null || @class.IsIncomplete) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (IsGetter(method)) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
var name = method.Name.Substring("get".Length); |
|
|
|
foreach (var setter in group) |
|
|
|
var prop = GetOrCreateProperty(@class, name, method.ReturnType); |
|
|
|
{ |
|
|
|
prop.GetMethod = method; |
|
|
|
Class type = (Class) setter.Namespace; |
|
|
|
prop.Access = method.Access; |
|
|
|
string afterSet = setter.Name.Substring(3); |
|
|
|
|
|
|
|
foreach (var getter in nonSetters.Where(m => m.Namespace == type)) |
|
|
|
// Do not generate the original method now that we know it is a getter.
|
|
|
|
{ |
|
|
|
method.IsGenerated = false; |
|
|
|
if (string.Compare(getter.Name, afterSet, StringComparison.OrdinalIgnoreCase) == 0 && |
|
|
|
|
|
|
|
getter.ReturnType == setter.Parameters[0].QualifiedType && |
|
|
|
Driver.Diagnostics.EmitMessage(DiagnosticId.PropertySynthetized, |
|
|
|
!type.Methods.Any( |
|
|
|
"Getter created: {0}::{1}", @class.Name, name); |
|
|
|
m => |
|
|
|
|
|
|
|
m != getter && |
|
|
|
return false; |
|
|
|
string.Compare(getter.Name, m.Name, StringComparison.OrdinalIgnoreCase) == 0)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
GenerateProperty(getter.Namespace, getter, readOnly ? null : setter); |
|
|
|
|
|
|
|
goto next; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Property baseVirtualProperty = type.GetRootBaseProperty(new Property { Name = afterSet }); |
|
|
|
|
|
|
|
if (!type.IsInterface && baseVirtualProperty != null) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
bool isReadOnly = baseVirtualProperty.SetMethod == null; |
|
|
|
|
|
|
|
GenerateProperty(setter.Namespace, baseVirtualProperty.GetMethod, |
|
|
|
|
|
|
|
readOnly || isReadOnly ? null : setter); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
next: |
|
|
|
|
|
|
|
; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
foreach (Method nonSetter in nonSetters) |
|
|
|
if (IsSetter(method) && IsValidSetter(method)) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
var name = method.Name.Substring("set".Length); |
|
|
|
Class type = (Class) nonSetter.Namespace; |
|
|
|
|
|
|
|
string name = nonSetter.Name; |
|
|
|
var type = method.Parameters[0].QualifiedType; |
|
|
|
if (GetFirstWord(name) == "get") |
|
|
|
var prop = GetOrCreateProperty(@class, name, type); |
|
|
|
name = name.Substring(3); |
|
|
|
prop.SetMethod = method; |
|
|
|
Property baseVirtualProperty = type.GetRootBaseProperty(new Property { Name = name }); |
|
|
|
prop.Access = method.Access; |
|
|
|
if (!type.IsInterface && baseVirtualProperty != null) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
bool isReadOnly = baseVirtualProperty.SetMethod == null; |
|
|
|
|
|
|
|
if (readOnly == isReadOnly) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
GenerateProperty(nonSetter.Namespace, nonSetter, |
|
|
|
|
|
|
|
readOnly ? null : baseVirtualProperty.SetMethod); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Ignore the original method now that we know it is a setter.
|
|
|
|
private static void GenerateProperty(DeclarationContext context, Method getter, Method setter = null) |
|
|
|
method.IsGenerated = false; |
|
|
|
{ |
|
|
|
|
|
|
|
Class type = (Class) context; |
|
|
|
|
|
|
|
if (type.Properties.All( |
|
|
|
|
|
|
|
p => string.Compare(getter.Name, p.Name, StringComparison.OrdinalIgnoreCase) != 0 || |
|
|
|
|
|
|
|
p.ExplicitInterfaceImpl != getter.ExplicitInterfaceImpl)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Property property = new Property(); |
|
|
|
|
|
|
|
property.Name = getter.Name.Substring(GetFirstWord(getter.Name) == "get" ? 3 : 0); |
|
|
|
|
|
|
|
property.Namespace = type; |
|
|
|
|
|
|
|
property.QualifiedType = getter.ReturnType; |
|
|
|
|
|
|
|
if (getter.IsOverride || (setter != null && setter.IsOverride)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Property baseVirtualProperty = type.GetRootBaseProperty(property); |
|
|
|
|
|
|
|
if (baseVirtualProperty.SetMethod == null) |
|
|
|
|
|
|
|
setter = null; |
|
|
|
|
|
|
|
foreach (Method method in type.Methods.Where(m => m.Name == property.Name && m.Parameters.Count > 0)) |
|
|
|
|
|
|
|
method.Name = "Get" + method.Name; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
property.GetMethod = getter; |
|
|
|
|
|
|
|
property.SetMethod = setter; |
|
|
|
|
|
|
|
property.ExplicitInterfaceImpl = getter.ExplicitInterfaceImpl; |
|
|
|
|
|
|
|
if (property.ExplicitInterfaceImpl == null && setter != null) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
property.ExplicitInterfaceImpl = setter.ExplicitInterfaceImpl; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// TODO: add comments
|
|
|
|
|
|
|
|
type.Properties.Add(property); |
|
|
|
|
|
|
|
getter.IsGenerated = false; |
|
|
|
|
|
|
|
if (setter != null) |
|
|
|
|
|
|
|
setter.IsGenerated = false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Driver.Diagnostics.EmitMessage(DiagnosticId.PropertySynthetized, |
|
|
|
|
|
|
|
"Setter created: {0}::{1}", @class.Name, name); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
private void DistributeMethod(Method method) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (GetFirstWord(method.Name) == "set" && |
|
|
|
|
|
|
|
method.ReturnType.Type.IsPrimitiveType(PrimitiveType.Void)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (method.Parameters.Count == 1) |
|
|
|
|
|
|
|
setters.Add(method); |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
setMethods.Add(method); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (IsGetter(method)) |
|
|
|
|
|
|
|
getters.Add(method); |
|
|
|
|
|
|
|
if (method.Parameters.Count == 0) |
|
|
|
|
|
|
|
nonSetters.Add(method); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Check if a matching getter exist or no other setter exists.
|
|
|
|
private bool IsGetter(Method method) |
|
|
|
private bool IsValidSetter(Method method) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
var @class = method.Namespace as Class; |
|
|
|
if (method.ReturnType.Type.IsPrimitiveType(PrimitiveType.Void) || |
|
|
|
var name = method.Name.Substring("set".Length); |
|
|
|
method.Parameters.Count > 0 || method.IsDestructor) |
|
|
|
|
|
|
|
|
|
|
|
if (method.Parameters.Count == 0) |
|
|
|
|
|
|
|
return false; |
|
|
|
return false; |
|
|
|
|
|
|
|
var result = GetFirstWord(method.Name); |
|
|
|
|
|
|
|
return result == "get" || result == "is" || result == "has" || |
|
|
|
|
|
|
|
(result != "to" && result != "new" && !verbs.Contains(result)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var type = method.Parameters[0].Type; |
|
|
|
private static string GetFirstWord(string name) |
|
|
|
|
|
|
|
{ |
|
|
|
var getter = @class.Methods.FirstOrDefault(m => m.Name == "Get" + name && m.Type.Equals(type)); |
|
|
|
List<char> firstVerb = new List<char> |
|
|
|
|
|
|
|
{ |
|
|
|
var otherSetter = @class.Methods.FirstOrDefault(m => m.Name == method.Name |
|
|
|
char.ToLowerInvariant(name[0]) |
|
|
|
&& m.Parameters.Count == 1 |
|
|
|
}; |
|
|
|
&& !m.Parameters[0].Type.Equals(type)); |
|
|
|
firstVerb.AddRange(name.Skip(1).TakeWhile(char.IsLower)); |
|
|
|
|
|
|
|
return new string(firstVerb.ToArray()); |
|
|
|
return getter != null || otherSetter == null; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|