|
|
|
@ -13,363 +13,345 @@ namespace CppSharp.Passes |
|
|
|
{ |
|
|
|
{ |
|
|
|
public class GetterSetterToPropertyPass : TranslationUnitPass |
|
|
|
public class GetterSetterToPropertyPass : TranslationUnitPass |
|
|
|
{ |
|
|
|
{ |
|
|
|
private class PropertyGenerator |
|
|
|
static GetterSetterToPropertyPass() |
|
|
|
{ |
|
|
|
{ |
|
|
|
private readonly List<Method> getters = new List<Method>(); |
|
|
|
LoadVerbs(); |
|
|
|
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 bool useHeuristics = true; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public PropertyGenerator(Class @class, bool useHeuristics) |
|
|
|
private static void LoadVerbs() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var assembly = Assembly.GetAssembly(typeof(GetterSetterToPropertyPass)); |
|
|
|
|
|
|
|
using (var resourceStream = GetResourceStream(assembly)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
this.useHeuristics = useHeuristics; |
|
|
|
using (var streamReader = new StreamReader(resourceStream)) |
|
|
|
foreach (var method in @class.Methods.Where( |
|
|
|
while (!streamReader.EndOfStream) |
|
|
|
m => !m.IsConstructor && !m.IsDestructor && !m.IsOperator && m.IsGenerated && |
|
|
|
verbs.Add(streamReader.ReadLine()); |
|
|
|
!m.ExcludeFromPasses.Contains(typeof(GetterSetterToPropertyPass)))) |
|
|
|
|
|
|
|
DistributeMethod(method); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void GenerateProperties() |
|
|
|
private static Stream GetResourceStream(Assembly assembly) |
|
|
|
{ |
|
|
|
{ |
|
|
|
GenerateProperties(setters, false); |
|
|
|
var resources = assembly.GetManifestResourceNames(); |
|
|
|
GenerateProperties(setMethods, true); |
|
|
|
|
|
|
|
|
|
|
|
if (resources.Count() == 0) |
|
|
|
foreach (Method getter in |
|
|
|
throw new Exception("Cannot find embedded verbs data resource."); |
|
|
|
from getter in getters |
|
|
|
|
|
|
|
where getter.IsGenerated && |
|
|
|
// We are relying on this fact that there is only one resource embedded.
|
|
|
|
getter.SynthKind != FunctionSynthKind.ComplementOperator && |
|
|
|
// Before we loaded the resource by name but found out that naming was
|
|
|
|
((Class) getter.Namespace).Methods.All( |
|
|
|
// different between different platforms and/or build systems.
|
|
|
|
m => m == getter || !m.IsGenerated || m.Name != getter.Name || |
|
|
|
return assembly.GetManifestResourceStream(resources[0]); |
|
|
|
m.Parameters.Count(p => p.Kind == ParameterKind.Regular) == 0) |
|
|
|
} |
|
|
|
select getter) |
|
|
|
|
|
|
|
{ |
|
|
|
public GetterSetterToPropertyPass() |
|
|
|
// Make it a read-only property
|
|
|
|
{ |
|
|
|
GenerateProperty(getter.Namespace, getter); |
|
|
|
VisitOptions.VisitClassBases = false; |
|
|
|
} |
|
|
|
VisitOptions.VisitClassFields = false; |
|
|
|
} |
|
|
|
VisitOptions.VisitClassProperties = false; |
|
|
|
|
|
|
|
VisitOptions.VisitClassMethods = false; |
|
|
|
|
|
|
|
VisitOptions.VisitNamespaceEnums = false; |
|
|
|
|
|
|
|
VisitOptions.VisitNamespaceTemplates = false; |
|
|
|
|
|
|
|
VisitOptions.VisitNamespaceTypedefs = false; |
|
|
|
|
|
|
|
VisitOptions.VisitNamespaceEvents = false; |
|
|
|
|
|
|
|
VisitOptions.VisitNamespaceVariables = false; |
|
|
|
|
|
|
|
VisitOptions.VisitFunctionParameters = false; |
|
|
|
|
|
|
|
VisitOptions.VisitTemplateArguments = false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void GenerateProperties(IEnumerable<Method> settersToUse, bool readOnly) |
|
|
|
public override bool VisitClassDecl(Class @class) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (!base.VisitClassDecl(@class)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ProcessProperties(@class, GenerateProperties(@class)); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected virtual HashSet<Property> GenerateProperties(Class @class) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var newProperties = new HashSet<Property>(); |
|
|
|
|
|
|
|
foreach (var method in @class.Methods.Where( |
|
|
|
|
|
|
|
m => !m.IsConstructor && !m.IsDestructor && !m.IsOperator && m.IsGenerated && |
|
|
|
|
|
|
|
m.SynthKind != FunctionSynthKind.DefaultValueOverload && |
|
|
|
|
|
|
|
m.SynthKind != FunctionSynthKind.ComplementOperator && |
|
|
|
|
|
|
|
!m.ExcludeFromPasses.Contains(typeof(GetterSetterToPropertyPass)))) |
|
|
|
{ |
|
|
|
{ |
|
|
|
foreach (var setter in settersToUse) |
|
|
|
if (IsGetter(method)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
var type = (Class) setter.Namespace; |
|
|
|
string name = GetPropertyName(method.Name); |
|
|
|
var firstWord = GetFirstWord(setter.Name); |
|
|
|
QualifiedType type = method.OriginalReturnType; |
|
|
|
string property; |
|
|
|
Property property = GetProperty(method, name, type); |
|
|
|
if ((firstWord == "set" || firstWord == "set_") && |
|
|
|
if (property.GetMethod == null) |
|
|
|
firstWord.Length < setter.Name.Length) |
|
|
|
|
|
|
|
property = setter.Name.Substring(firstWord.Length); |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
property = setter.Name; |
|
|
|
|
|
|
|
var nameBuilder = new StringBuilder(property); |
|
|
|
|
|
|
|
if (char.IsLower(setter.Name[0])) |
|
|
|
|
|
|
|
nameBuilder[0] = char.ToLowerInvariant(nameBuilder[0]); |
|
|
|
|
|
|
|
string afterSet = nameBuilder.ToString(); |
|
|
|
|
|
|
|
var s = setter; |
|
|
|
|
|
|
|
foreach (var getter in nonSetters.Where(m => m.Namespace == type && |
|
|
|
|
|
|
|
m.ExplicitInterfaceImpl == s.ExplicitInterfaceImpl)) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
var name = GetReadWritePropertyName(getter, afterSet); |
|
|
|
property.GetMethod = method; |
|
|
|
if (name == afterSet && |
|
|
|
property.QualifiedType = method.OriginalReturnType; |
|
|
|
GetUnderlyingType(getter.OriginalReturnType).Equals( |
|
|
|
newProperties.Add(property); |
|
|
|
GetUnderlyingType(setter.Parameters[0].QualifiedType))) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Method g = getter; |
|
|
|
|
|
|
|
foreach (var method in type.Methods.Where(m => m != g && m.Name == name)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var oldName = method.Name; |
|
|
|
|
|
|
|
method.Name = string.Format("get{0}{1}", |
|
|
|
|
|
|
|
char.ToUpperInvariant(method.Name[0]), method.Name.Substring(1)); |
|
|
|
|
|
|
|
Diagnostics.Debug("Method {0}::{1} renamed to {2}", method.Namespace.Name, oldName, method.Name); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
foreach (var @event in type.Events.Where(e => e.Name == name)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var oldName = @event.Name; |
|
|
|
|
|
|
|
@event.Name = string.Format("on{0}{1}", |
|
|
|
|
|
|
|
char.ToUpperInvariant(@event.Name[0]), @event.Name.Substring(1)); |
|
|
|
|
|
|
|
Diagnostics.Debug("Event {0}::{1} renamed to {2}", @event.Namespace.Name, oldName, @event.Name); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
GenerateProperty(name, getter.Namespace, getter, readOnly ? null : setter); |
|
|
|
|
|
|
|
goto next; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
Property baseProperty = type.GetBaseProperty(new Property { Name = afterSet }, getTopmost: true); |
|
|
|
else |
|
|
|
if (!type.IsInterface && baseProperty != null && baseProperty.IsVirtual && setter.IsVirtual) |
|
|
|
method.GenerationKind = GenerationKind.Generate; |
|
|
|
{ |
|
|
|
continue; |
|
|
|
bool isReadOnly = baseProperty.SetMethod == null; |
|
|
|
|
|
|
|
var name = GetReadWritePropertyName(baseProperty.GetMethod, afterSet); |
|
|
|
|
|
|
|
GenerateProperty(name, setter.Namespace, baseProperty.GetMethod, |
|
|
|
|
|
|
|
readOnly || isReadOnly ? null : setter); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
next: |
|
|
|
|
|
|
|
; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
foreach (Method nonSetter in nonSetters) |
|
|
|
if (IsSetter(method)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Class type = (Class) nonSetter.Namespace; |
|
|
|
string name = GetPropertyNameFromSetter(method.Name); |
|
|
|
string name = GetPropertyName(nonSetter.Name); |
|
|
|
QualifiedType type = method.Parameters.First(p => p.Kind == ParameterKind.Regular).QualifiedType; |
|
|
|
Property baseProperty = type.GetBaseProperty(new Property { Name = name }, getTopmost: true); |
|
|
|
Property property = GetProperty(method, name, type); |
|
|
|
if (!type.IsInterface && baseProperty != null && baseProperty.IsVirtual) |
|
|
|
property.SetMethod = method; |
|
|
|
{ |
|
|
|
newProperties.Add(property); |
|
|
|
bool isReadOnly = baseProperty.SetMethod == null; |
|
|
|
|
|
|
|
if (readOnly == isReadOnly) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
GenerateProperty(nonSetter.Namespace, nonSetter, |
|
|
|
|
|
|
|
readOnly ? null : baseProperty.SetMethod); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static string GetReadWritePropertyName(INamedDecl getter, string afterSet) |
|
|
|
return newProperties; |
|
|
|
{ |
|
|
|
} |
|
|
|
string name = GetPropertyName(getter.Name); |
|
|
|
|
|
|
|
if (name != afterSet && name.StartsWith("is", StringComparison.Ordinal) && |
|
|
|
private static Property GetProperty(Method method, string name, QualifiedType type) |
|
|
|
name != "is") |
|
|
|
{ |
|
|
|
{ |
|
|
|
Type underlyingType = GetUnderlyingType(type); |
|
|
|
name = char.ToLowerInvariant(name[2]) + name.Substring(3); |
|
|
|
Class @class = (Class) method.Namespace; |
|
|
|
} |
|
|
|
Property property = @class.Properties.Find( |
|
|
|
return name; |
|
|
|
p => p.Field == null && |
|
|
|
} |
|
|
|
(p.Name == name || |
|
|
|
|
|
|
|
(p.GetMethod != null && GetReadWritePropertyName(p.GetMethod, name) == name)) && |
|
|
|
|
|
|
|
((p.GetMethod != null && |
|
|
|
|
|
|
|
GetUnderlyingType(p.GetMethod.OriginalReturnType).Equals(underlyingType)) || |
|
|
|
|
|
|
|
(p.SetMethod != null && |
|
|
|
|
|
|
|
GetUnderlyingType(p.SetMethod.Parameters[0].QualifiedType).Equals(underlyingType)))) ?? |
|
|
|
|
|
|
|
new Property { Name = name, QualifiedType = type }; |
|
|
|
|
|
|
|
|
|
|
|
private static Type GetUnderlyingType(QualifiedType type) |
|
|
|
if (property.Namespace == null) |
|
|
|
{ |
|
|
|
{ |
|
|
|
TagType tagType = type.Type as TagType; |
|
|
|
property.Namespace = method.Namespace; |
|
|
|
if (tagType != null) |
|
|
|
property.Access = method.Access; |
|
|
|
return type.Type; |
|
|
|
@class.Properties.Add(property); |
|
|
|
// TODO: we should normally check pointer types for const;
|
|
|
|
|
|
|
|
// however, there's some bug, probably in the parser, that returns IsConst = false for "const Type& arg"
|
|
|
|
|
|
|
|
// so skip the check for the time being
|
|
|
|
|
|
|
|
PointerType pointerType = type.Type as PointerType; |
|
|
|
|
|
|
|
return pointerType != null ? pointerType.Pointee : type.Type; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
private static void GenerateProperty(DeclarationContext context, Method getter, Method setter = null) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
GenerateProperty(GetPropertyName(getter.Name), context, getter, setter); |
|
|
|
property.Access = (AccessSpecifier) Math.Max( |
|
|
|
|
|
|
|
(int) (property.GetMethod ?? property.SetMethod).Access, |
|
|
|
|
|
|
|
(int) method.Access); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static void GenerateProperty(string name, DeclarationContext context, Method getter, Method setter) |
|
|
|
property.Name = property.OriginalName = name; |
|
|
|
{ |
|
|
|
method.GenerationKind = GenerationKind.Internal; |
|
|
|
var type = (Class) context; |
|
|
|
if (method.ExplicitInterfaceImpl != null) |
|
|
|
if (type.Properties.Any(p => p.Name == name && |
|
|
|
property.ExplicitInterfaceImpl = method.ExplicitInterfaceImpl; |
|
|
|
p.ExplicitInterfaceImpl == getter.ExplicitInterfaceImpl)) |
|
|
|
return property; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var property = new Property |
|
|
|
private static void ProcessProperties(Class @class, HashSet<Property> newProperties) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Access = getter.Access == AccessSpecifier.Public || |
|
|
|
foreach (var property in newProperties) |
|
|
|
(setter != null && setter.Access == AccessSpecifier.Public) ? |
|
|
|
{ |
|
|
|
AccessSpecifier.Public : AccessSpecifier.Protected, |
|
|
|
if (property.IsOverride) |
|
|
|
Name = name, |
|
|
|
|
|
|
|
Namespace = type, |
|
|
|
|
|
|
|
QualifiedType = getter.OriginalReturnType, |
|
|
|
|
|
|
|
OriginalNamespace = getter.OriginalNamespace |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
if (getter.IsOverride || (setter != null && setter.IsOverride)) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
var baseVirtualProperty = type.GetBaseProperty(property, getTopmost: true); |
|
|
|
Property baseProperty = GetBaseProperty(@class, property); |
|
|
|
if (baseVirtualProperty != null && !baseVirtualProperty.IsVirtual) |
|
|
|
if (baseProperty == null) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// the only way the above can happen is if we are generating properties in abstract implementations
|
|
|
|
if (property.SetMethod != null) |
|
|
|
// in which case we can have less naming conflicts since the abstract base can also contain non-virtual properties
|
|
|
|
{ |
|
|
|
if (getter.SynthKind == FunctionSynthKind.AbstractImplCall) |
|
|
|
property.SetMethod.GenerationKind = GenerationKind.Generate; |
|
|
|
return; |
|
|
|
property.SetMethod = null; |
|
|
|
throw new Exception(string.Format( |
|
|
|
} |
|
|
|
"Base of property {0} is not virtual while the getter is.", |
|
|
|
else |
|
|
|
getter.QualifiedOriginalName)); |
|
|
|
{ |
|
|
|
|
|
|
|
property.GetMethod.GenerationKind = GenerationKind.Generate; |
|
|
|
|
|
|
|
property.GetMethod = null; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (baseVirtualProperty == null || baseVirtualProperty.SetMethod == null) |
|
|
|
else if (property.GetMethod == null && baseProperty.SetMethod != null) |
|
|
|
setter = null; |
|
|
|
property.GetMethod = baseProperty.GetMethod; |
|
|
|
} |
|
|
|
else if (property.SetMethod == null) |
|
|
|
property.GetMethod = getter; |
|
|
|
property.SetMethod = baseProperty.SetMethod; |
|
|
|
property.SetMethod = setter; |
|
|
|
|
|
|
|
property.ExplicitInterfaceImpl = getter.ExplicitInterfaceImpl; |
|
|
|
|
|
|
|
if (property.ExplicitInterfaceImpl == null && setter != null) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
property.ExplicitInterfaceImpl = setter.ExplicitInterfaceImpl; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
if (getter.Comment != null) |
|
|
|
if (property.GetMethod == null) |
|
|
|
{ |
|
|
|
{ |
|
|
|
property.Comment = CombineComments(getter, setter); |
|
|
|
if (property.SetMethod != null) |
|
|
|
|
|
|
|
property.SetMethod.GenerationKind = GenerationKind.Generate; |
|
|
|
|
|
|
|
@class.Properties.Remove(property); |
|
|
|
|
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
type.Properties.Add(property); |
|
|
|
|
|
|
|
getter.GenerationKind = GenerationKind.Internal; |
|
|
|
|
|
|
|
if (setter != null) |
|
|
|
|
|
|
|
setter.GenerationKind = GenerationKind.Internal; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static RawComment CombineComments(Declaration getter, Declaration setter) |
|
|
|
foreach (var method in @class.Methods.Where( |
|
|
|
{ |
|
|
|
m => m.IsGenerated && m.Name == property.Name)) |
|
|
|
var comment = new RawComment |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Kind = getter.Comment.Kind, |
|
|
|
|
|
|
|
BriefText = getter.Comment.BriefText, |
|
|
|
|
|
|
|
Text = getter.Comment.Text |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
if (getter.Comment.FullComment != null) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
comment.FullComment = new FullComment(); |
|
|
|
var oldName = method.Name; |
|
|
|
comment.FullComment.Blocks.AddRange(getter.Comment.FullComment.Blocks); |
|
|
|
method.Name = $@"get{char.ToUpperInvariant(method.Name[0])}{
|
|
|
|
if (getter != setter && setter != null && setter.Comment != null) |
|
|
|
method.Name.Substring(1)}";
|
|
|
|
{ |
|
|
|
Diagnostics.Debug("Method {0}::{1} renamed to {2}", |
|
|
|
comment.BriefText += Environment.NewLine + setter.Comment.BriefText; |
|
|
|
method.Namespace.Name, oldName, method.Name); |
|
|
|
comment.Text += Environment.NewLine + setter.Comment.Text; |
|
|
|
|
|
|
|
comment.FullComment.Blocks.AddRange(setter.Comment.FullComment.Blocks); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
return comment; |
|
|
|
foreach (var @event in @class.Events.Where( |
|
|
|
} |
|
|
|
e => e.Name == property.Name)) |
|
|
|
|
|
|
|
|
|
|
|
private static string GetPropertyName(string name) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var firstWord = GetFirstWord(name); |
|
|
|
|
|
|
|
if (Match(firstWord, new[] { "get" }) && name != firstWord && |
|
|
|
|
|
|
|
!char.IsNumber(name[3])) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
if (char.IsLower(name[0])) |
|
|
|
var oldName = @event.Name; |
|
|
|
{ |
|
|
|
@event.Name = $@"on{char.ToUpperInvariant(@event.Name[0])}{
|
|
|
|
if (name.Length == 4) |
|
|
|
@event.Name.Substring(1)}";
|
|
|
|
{ |
|
|
|
Diagnostics.Debug("Event {0}::{1} renamed to {2}", |
|
|
|
return char.ToLowerInvariant( |
|
|
|
@event.Namespace.Name, oldName, @event.Name); |
|
|
|
name[3]).ToString(CultureInfo.InvariantCulture); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return char.ToLowerInvariant( |
|
|
|
|
|
|
|
name[3]).ToString(CultureInfo.InvariantCulture) + |
|
|
|
|
|
|
|
name.Substring(4); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return name.Substring(3); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
return name; |
|
|
|
CombineComments(property); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static string GetPropertyNameFromSetter(Method setter) |
|
|
|
private static Property GetBaseProperty(Class @class, Property @override) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
foreach (var @base in @class.Bases) |
|
|
|
{ |
|
|
|
{ |
|
|
|
var name = setter.Name.Substring("set".Length); |
|
|
|
Class baseClass = @base.Class.OriginalClass ?? @base.Class; |
|
|
|
if (string.IsNullOrEmpty(name)) |
|
|
|
Property baseProperty = baseClass.Properties.Find(p => |
|
|
|
return name; |
|
|
|
(@override.GetMethod != null && @override.GetMethod.BaseMethod == p.GetMethod) || |
|
|
|
if (char.IsLower(setter.Name[0]) && !char.IsLower(name[0])) |
|
|
|
(@override.SetMethod != null && @override.SetMethod.BaseMethod == p.SetMethod) || |
|
|
|
return char.ToLowerInvariant(name[0]) + name.Substring(1); |
|
|
|
(@override.Field != null && @override.Field == p.Field)); |
|
|
|
return name; |
|
|
|
if (baseProperty != null) |
|
|
|
} |
|
|
|
return baseProperty; |
|
|
|
|
|
|
|
|
|
|
|
private void DistributeMethod(Method method) |
|
|
|
baseProperty = GetBaseProperty(@base.Class, @override); |
|
|
|
{ |
|
|
|
if (baseProperty != null) |
|
|
|
Type returnType = method.OriginalReturnType.Type.Desugar(); |
|
|
|
return baseProperty; |
|
|
|
if ((returnType.IsPrimitiveType(PrimitiveType.Void) || |
|
|
|
|
|
|
|
returnType.IsPrimitiveType(PrimitiveType.Bool)) && |
|
|
|
|
|
|
|
method.Parameters.Any(p => p.Kind == ParameterKind.Regular)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (method.Parameters.Count == 1) |
|
|
|
|
|
|
|
setters.Add(method); |
|
|
|
|
|
|
|
else if (method.Parameters.Count > 1) |
|
|
|
|
|
|
|
setMethods.Add(method); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (method.ConvertToProperty || IsGetter(method)) |
|
|
|
|
|
|
|
getters.Add(method); |
|
|
|
|
|
|
|
if (method.Parameters.All(p => p.Kind == ParameterKind.IndirectReturnType)) |
|
|
|
|
|
|
|
nonSetters.Add(method); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private bool IsGetter(Method method) |
|
|
|
private static string GetReadWritePropertyName(INamedDecl getter, string afterSet) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
string name = GetPropertyName(getter.Name); |
|
|
|
|
|
|
|
if (name != afterSet && name.StartsWith("is", StringComparison.Ordinal) && |
|
|
|
|
|
|
|
name != "is") |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (method.IsDestructor || |
|
|
|
name = char.ToLowerInvariant(name[2]) + name.Substring(3); |
|
|
|
(method.OriginalReturnType.Type.IsPrimitiveType(PrimitiveType.Void)) || |
|
|
|
} |
|
|
|
method.Parameters.Any(p => p.Kind != ParameterKind.IndirectReturnType)) |
|
|
|
return name; |
|
|
|
return false; |
|
|
|
} |
|
|
|
var firstWord = GetFirstWord(method.Name); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (firstWord.Length < method.Name.Length && Match(firstWord, new[] {"get", "is", "has"})) |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (useHeuristics && !Match(firstWord, new[] {"to", "new"}) && !verbs.Contains(firstWord)) |
|
|
|
private static Type GetUnderlyingType(QualifiedType type) |
|
|
|
return true; |
|
|
|
{ |
|
|
|
|
|
|
|
TagType tagType = type.Type as TagType; |
|
|
|
|
|
|
|
if (tagType != null) |
|
|
|
|
|
|
|
return type.Type; |
|
|
|
|
|
|
|
// TODO: we should normally check pointer types for const;
|
|
|
|
|
|
|
|
// however, there's some bug, probably in the parser, that returns IsConst = false for "const Type& arg"
|
|
|
|
|
|
|
|
// so skip the check for the time being
|
|
|
|
|
|
|
|
PointerType pointerType = type.Type as PointerType; |
|
|
|
|
|
|
|
return pointerType != null ? pointerType.Pointee : type.Type; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
private static void CombineComments(Property property) |
|
|
|
} |
|
|
|
{ |
|
|
|
|
|
|
|
Method getter = property.GetMethod; |
|
|
|
|
|
|
|
if (getter.Comment == null) |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
private static bool Match(string prefix, IEnumerable<string> prefixes) |
|
|
|
var comment = new RawComment |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Kind = getter.Comment.Kind, |
|
|
|
|
|
|
|
BriefText = getter.Comment.BriefText, |
|
|
|
|
|
|
|
Text = getter.Comment.Text |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
if (getter.Comment.FullComment != null) |
|
|
|
{ |
|
|
|
{ |
|
|
|
return prefixes.Any(p => prefix == p || prefix == p + '_'); |
|
|
|
comment.FullComment = new FullComment(); |
|
|
|
|
|
|
|
comment.FullComment.Blocks.AddRange(getter.Comment.FullComment.Blocks); |
|
|
|
|
|
|
|
Method setter = property.SetMethod; |
|
|
|
|
|
|
|
if (getter != setter && setter?.Comment != null) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
comment.BriefText += Environment.NewLine + setter.Comment.BriefText; |
|
|
|
|
|
|
|
comment.Text += Environment.NewLine + setter.Comment.Text; |
|
|
|
|
|
|
|
comment.FullComment.Blocks.AddRange(setter.Comment.FullComment.Blocks); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
property.Comment = comment; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static string GetFirstWord(string name) |
|
|
|
private static string GetPropertyName(string name) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var firstWord = GetFirstWord(name); |
|
|
|
|
|
|
|
if (Match(firstWord, new[] { "get" }) && name != firstWord && |
|
|
|
|
|
|
|
!char.IsNumber(name[3])) |
|
|
|
{ |
|
|
|
{ |
|
|
|
var firstWord = new List<char> { char.ToLowerInvariant(name[0]) }; |
|
|
|
if (char.IsLower(name[0])) |
|
|
|
for (int i = 1; i < name.Length; i++) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
var c = name[i]; |
|
|
|
if (name.Length == 4) |
|
|
|
if (char.IsLower(c)) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
firstWord.Add(c); |
|
|
|
return char.ToLowerInvariant( |
|
|
|
continue; |
|
|
|
name[3]).ToString(CultureInfo.InvariantCulture); |
|
|
|
} |
|
|
|
|
|
|
|
if (c == '_') |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
firstWord.Add(c); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
if (char.IsUpper(c)) |
|
|
|
return char.ToLowerInvariant( |
|
|
|
break; |
|
|
|
name[3]).ToString(CultureInfo.InvariantCulture) + |
|
|
|
|
|
|
|
name.Substring(4); |
|
|
|
} |
|
|
|
} |
|
|
|
return new string(firstWord.ToArray()); |
|
|
|
return name.Substring(3); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return name; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static readonly HashSet<string> verbs = new HashSet<string>(); |
|
|
|
private static string GetPropertyNameFromSetter(string name) |
|
|
|
|
|
|
|
|
|
|
|
static GetterSetterToPropertyPass() |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
LoadVerbs(); |
|
|
|
var nameBuilder = new StringBuilder(name); |
|
|
|
} |
|
|
|
string firstWord = GetFirstWord(name); |
|
|
|
|
|
|
|
if (firstWord == "set" || firstWord == "set_") |
|
|
|
|
|
|
|
nameBuilder.Remove(0, firstWord.Length); |
|
|
|
|
|
|
|
if (nameBuilder.Length == 0) |
|
|
|
|
|
|
|
return nameBuilder.ToString(); |
|
|
|
|
|
|
|
|
|
|
|
private static void LoadVerbs() |
|
|
|
nameBuilder.TrimUnderscores(); |
|
|
|
{ |
|
|
|
if (char.IsLower(name[0]) && !char.IsLower(nameBuilder[0])) |
|
|
|
var assembly = Assembly.GetAssembly(typeof(GetterSetterToPropertyPass)); |
|
|
|
nameBuilder[0] = char.ToLowerInvariant(nameBuilder[0]); |
|
|
|
using (var resourceStream = GetResourceStream(assembly)) |
|
|
|
return nameBuilder.ToString(); |
|
|
|
{ |
|
|
|
|
|
|
|
using (var streamReader = new StreamReader(resourceStream)) |
|
|
|
|
|
|
|
while (!streamReader.EndOfStream) |
|
|
|
|
|
|
|
verbs.Add(streamReader.ReadLine()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static Stream GetResourceStream(Assembly assembly) |
|
|
|
private bool IsGetter(Method method) |
|
|
|
{ |
|
|
|
{ |
|
|
|
var resources = assembly.GetManifestResourceNames(); |
|
|
|
if (method.IsDestructor || |
|
|
|
|
|
|
|
method.OriginalReturnType.Type.IsPrimitiveType(PrimitiveType.Void) || |
|
|
|
|
|
|
|
method.Parameters.Any(p => p.Kind != ParameterKind.IndirectReturnType)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
var firstWord = GetFirstWord(method.Name); |
|
|
|
|
|
|
|
|
|
|
|
if (resources.Count() == 0) |
|
|
|
if (firstWord.Length < method.Name.Length && |
|
|
|
throw new Exception("Cannot find embedded verbs data resource."); |
|
|
|
Match(firstWord, new[] { "get", "is", "has" })) |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
|
|
// We are relying on this fact that there is only one resource embedded.
|
|
|
|
if (Options.UsePropertyDetectionHeuristics && |
|
|
|
// Before we loaded the resource by name but found out that naming was
|
|
|
|
!Match(firstWord, new[] { "to", "new" }) && !verbs.Contains(firstWord)) |
|
|
|
// different between different platforms and/or build systems.
|
|
|
|
return true; |
|
|
|
return assembly.GetManifestResourceStream(resources[0]); |
|
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public GetterSetterToPropertyPass() |
|
|
|
private static bool IsSetter(Method method) |
|
|
|
{ |
|
|
|
{ |
|
|
|
VisitOptions.VisitClassBases = false; |
|
|
|
Type returnType = method.OriginalReturnType.Type.Desugar(); |
|
|
|
VisitOptions.VisitClassFields = false; |
|
|
|
return (returnType.IsPrimitiveType(PrimitiveType.Void) || |
|
|
|
VisitOptions.VisitClassProperties = false; |
|
|
|
returnType.IsPrimitiveType(PrimitiveType.Bool)) && |
|
|
|
VisitOptions.VisitClassMethods = false; |
|
|
|
method.Parameters.Count(p => p.Kind == ParameterKind.Regular) == 1; |
|
|
|
VisitOptions.VisitNamespaceEnums = false; |
|
|
|
|
|
|
|
VisitOptions.VisitNamespaceTemplates = false; |
|
|
|
|
|
|
|
VisitOptions.VisitNamespaceTypedefs = false; |
|
|
|
|
|
|
|
VisitOptions.VisitNamespaceEvents = false; |
|
|
|
|
|
|
|
VisitOptions.VisitNamespaceVariables = false; |
|
|
|
|
|
|
|
VisitOptions.VisitFunctionParameters = false; |
|
|
|
|
|
|
|
VisitOptions.VisitTemplateArguments = false; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public override bool VisitClassDecl(Class @class) |
|
|
|
private static bool Match(string prefix, IEnumerable<string> prefixes) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (base.VisitClassDecl(@class)) |
|
|
|
return prefixes.Any(p => prefix == p || prefix == p + '_'); |
|
|
|
new PropertyGenerator(@class, Options.UsePropertyDetectionHeuristics).GenerateProperties(); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static string GetFirstWord(string name) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var firstWord = new List<char> { char.ToLowerInvariant(name[0]) }; |
|
|
|
|
|
|
|
for (int i = 1; i < name.Length; i++) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var c = name[i]; |
|
|
|
|
|
|
|
if (char.IsLower(c)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
firstWord.Add(c); |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (c == '_') |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
firstWord.Add(c); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (char.IsUpper(c)) |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return new string(firstWord.ToArray()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static readonly HashSet<string> verbs = new HashSet<string>(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|