Browse Source

Rework property handling (#1714)

* Rewrite property detection logic to be more configurable.

* Add new property modes to the CLI driver.

* Refactor property handling in GetterSetterToProperty.
pull/1869/head
João Matos 10 months ago committed by GitHub
parent
commit
45d6b3279b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 23
      src/CLI/CLI.cs
  2. 1
      src/CLI/Generator.cs
  3. 3
      src/CLI/Options.cs
  4. 3
      src/Generator/Options.cs
  5. 100
      src/Generator/Passes/GetterSetterToPropertyPass.cs

23
src/CLI/CLI.cs

@ -3,6 +3,7 @@ using System.Collections.Generic; @@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using CppSharp.Generators;
using CppSharp.Passes;
using Mono.Options;
namespace CppSharp
@ -34,6 +35,7 @@ namespace CppSharp @@ -34,6 +35,7 @@ namespace CppSharp
optionSet.Add("p=|platform=", "the {PLATFORM} that the generated code will target: 'win', 'osx' or 'linux' or 'emscripten'", p => { GetDestinationPlatform(p, errorMessages); });
optionSet.Add("a=|arch=", "the {ARCHITECTURE} that the generated code will target: 'x86' or 'x64' or 'wasm32' or 'wasm64'", a => { GetDestinationArchitecture(a, errorMessages); });
optionSet.Add("prefix=", "sets a string prefix to the names of generated files", a => { options.Prefix = a; });
optionSet.Add("property=", "the property detection mode to use: 'all', 'none' or 'keywords' or 'heuristics'", p => { GetPropertyMode(p, errorMessages); });
optionSet.Add("exceptions", "enables support for C++ exceptions in the parser", v => { options.EnableExceptions = true; });
optionSet.Add("rtti", "enables support for C++ RTTI in the parser", v => { options.EnableRTTI = true; });
@ -269,6 +271,27 @@ namespace CppSharp @@ -269,6 +271,27 @@ namespace CppSharp
Defaulting to {options.Architecture}");
}
static void GetPropertyMode(string mode, List<string> errorMessages)
{
switch (mode.ToLower())
{
case "all":
options.PropertyMode = PropertyDetectionMode.All;
return;
case "none":
options.PropertyMode = PropertyDetectionMode.None;
return;
case "dictionary":
options.PropertyMode = PropertyDetectionMode.Dictionary;
return;
case "keywords":
options.PropertyMode = PropertyDetectionMode.Keywords;
return;
}
errorMessages.Add($"Unknown property detection mode: {mode}. Defaulting to {options.PropertyMode}");
}
static void PrintErrorMessages(List<string> errorMessages)
{
foreach (string m in errorMessages)

1
src/CLI/Generator.cs

@ -129,6 +129,7 @@ namespace CppSharp @@ -129,6 +129,7 @@ namespace CppSharp
var driverOptions = driver.Options;
driverOptions.GeneratorKind = options.Kind;
driverOptions.PropertyDetectionMode = options.PropertyMode;
var module = driverOptions.AddModule(options.OutputFileName);
if (!string.IsNullOrEmpty(options.InputLibraryName))

3
src/CLI/Options.cs

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
using System.Collections.Generic;
using CppSharp.Generators;
using CppSharp.Passes;
namespace CppSharp
{
@ -43,6 +44,8 @@ namespace CppSharp @@ -43,6 +44,8 @@ namespace CppSharp
public GeneratorKind Kind { get; set; } = GeneratorKind.CSharp;
public PropertyDetectionMode PropertyMode { get; set; } = PropertyDetectionMode.Keywords;
public bool CheckSymbols { get; set; }
public bool UnityBuild { get; set; }

3
src/Generator/Options.cs

@ -247,6 +247,9 @@ namespace CppSharp @@ -247,6 +247,9 @@ namespace CppSharp
/// </summary>
public HashSet<string> ExplicitlyPatchedVirtualFunctions { get; }
public PropertyDetectionMode PropertyDetectionMode { get; set; } = PropertyDetectionMode.Dictionary;
[Obsolete("Use PropertyDetectionMode instead")]
public bool UsePropertyDetectionHeuristics { get; set; } = true;
/// <summary>

100
src/Generator/Passes/GetterSetterToPropertyPass.cs

@ -11,6 +11,33 @@ using Type = CppSharp.AST.Type; @@ -11,6 +11,33 @@ using Type = CppSharp.AST.Type;
namespace CppSharp.Passes
{
/// <summary>
/// This is used by GetterSetterToPropertyPass to decide how to process
/// getter/setter class methods into properties.
/// </summary>
public enum PropertyDetectionMode
{
/// <summary>
/// No methods are converted to properties.
/// </summary>
None,
/// <summary>
/// All compatible methods are converted to properties.
/// </summary>
All,
/// <summary>
/// Only methods starting with certain keyword are converted to properties.
/// Right now we consider getter methods starting with "get", "is" and "has".
/// </summary>
Keywords,
/// <summary>
/// Heuristics based mode that uses english dictionary words to decide
/// if a getter method is an action and thus not to be considered as a
/// property.
/// </summary>
Dictionary
}
public class GetterSetterToPropertyPass : TranslationUnitPass
{
static GetterSetterToPropertyPass()
@ -44,6 +71,9 @@ namespace CppSharp.Passes @@ -44,6 +71,9 @@ namespace CppSharp.Passes
public override bool VisitClassDecl(Class @class)
{
if (Options.PropertyDetectionMode == PropertyDetectionMode.None)
return false;
if (!base.VisitClassDecl(@class))
return false;
@ -86,35 +116,58 @@ namespace CppSharp.Passes @@ -86,35 +116,58 @@ namespace CppSharp.Passes
private IEnumerable<Property> CleanUp(Class @class, List<Property> properties)
{
if (!Options.UsePropertyDetectionHeuristics)
#pragma warning disable CS0618
if (!Options.UsePropertyDetectionHeuristics ||
#pragma warning restore CS0618
Options.PropertyDetectionMode == PropertyDetectionMode.All)
return properties;
for (int i = properties.Count - 1; i >= 0; i--)
{
Property property = properties[i];
if (property.HasSetter || property.IsExplicitlyGenerated)
continue;
string firstWord = GetFirstWord(property.GetMethod.Name);
if (firstWord.Length < property.GetMethod.Name.Length &&
Match(firstWord, new[] { "get", "is", "has" }))
var property = properties[i];
if (KeepProperty(property))
continue;
if (Match(firstWord, new[] { "to", "new", "on" }) ||
Verbs.Contains(firstWord))
{
property.GetMethod.GenerationKind = GenerationKind.Generate;
@class.Properties.Remove(property);
properties.RemoveAt(i);
}
property.GetMethod.GenerationKind = GenerationKind.Generate;
@class.Properties.Remove(property);
properties.RemoveAt(i);
}
return properties;
}
public virtual bool KeepProperty(Property property)
{
if (property.HasSetter || property.IsExplicitlyGenerated)
return true;
var firstWord = GetFirstWord(property.GetMethod.Name);
var isKeyword = firstWord.Length < property.GetMethod.Name.Length &&
Match(firstWord, new[] {"get", "is", "has"});
switch (Options.PropertyDetectionMode)
{
case PropertyDetectionMode.Keywords:
return isKeyword;
case PropertyDetectionMode.Dictionary:
var isAction = Match(firstWord, new[] {"to", "new", "on"}) || Verbs.Contains(firstWord);
return isKeyword || !isAction;
default:
return false;
}
}
private static void CreateOrUpdateProperty(List<Property> properties, Method method,
string name, QualifiedType type, bool isSetter = false)
{
string NormalizeName(string name)
{
return string.IsNullOrEmpty(name) ?
name : string.Concat(char.ToLowerInvariant(name[0]), name.Substring(1));
}
var normalizedName = NormalizeName(name);
Type underlyingType = GetUnderlyingType(type);
Property property = properties.Find(
p => p.Field == null &&
@ -124,10 +177,10 @@ namespace CppSharp.Passes @@ -124,10 +177,10 @@ namespace CppSharp.Passes
p.GetMethod.OriginalReturnType).Equals(underlyingType)) ||
(p.HasSetter && GetUnderlyingType(
p.SetMethod.Parameters[0].QualifiedType).Equals(underlyingType))) &&
Match(p, name));
Match(p, normalizedName));
if (property == null)
properties.Add(property = new Property { Name = name, QualifiedType = type });
properties.Add(property = new Property { Name = normalizedName, QualifiedType = type });
method.AssociatedDeclaration = property;
@ -201,7 +254,9 @@ namespace CppSharp.Passes @@ -201,7 +254,9 @@ namespace CppSharp.Passes
property.SetMethod.OriginalReturnType.Type.Desugar().IsPrimitiveType(PrimitiveType.Void))
property.SetMethod.GenerationKind = GenerationKind.Internal;
property.Namespace = @class;
@class.Properties.Add(property);
RenameConflictingMethods(@class, property);
CombineComments(property);
}
@ -294,14 +349,8 @@ namespace CppSharp.Passes @@ -294,14 +349,8 @@ namespace CppSharp.Passes
(string.Compare(name, firstWord, StringComparison.InvariantCultureIgnoreCase) == 0) ||
char.IsNumber(name[3])) return name;
if (name.Length == 4)
{
return char.ToLowerInvariant(
name[3]).ToString(CultureInfo.InvariantCulture);
}
return string.Concat(char.ToLowerInvariant(
name[3]).ToString(CultureInfo.InvariantCulture), name.AsSpan(4));
var rest = (name.Length == 4) ? string.Empty : name.Substring(4);
return string.Concat(name[3], rest);
}
private static string GetPropertyNameFromSetter(string name)
@ -314,7 +363,6 @@ namespace CppSharp.Passes @@ -314,7 +363,6 @@ namespace CppSharp.Passes
return nameBuilder.ToString();
nameBuilder.TrimUnderscores();
nameBuilder[0] = char.ToLowerInvariant(nameBuilder[0]);
return nameBuilder.ToString();
}

Loading…
Cancel
Save