mirror of https://github.com/mono/CppSharp.git
c-sharpdotnetmonobindingsbridgecclangcpluspluscppsharpglueinteropparserparsingpinvokeswigsyntax-treevisitorsxamarinxamarin-bindings
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
338 lines
12 KiB
338 lines
12 KiB
using System.Collections.Generic; |
|
using System.Linq; |
|
using System.Text; |
|
using System.Text.RegularExpressions; |
|
using CppSharp.AST; |
|
using CppSharp.AST.Extensions; |
|
using CppSharp.Generators; |
|
using CppSharp.Generators.CSharp; |
|
using CppSharp.Types; |
|
|
|
namespace CppSharp.Passes |
|
{ |
|
public class HandleDefaultParamValuesPass : TranslationUnitPass |
|
{ |
|
private static readonly Regex regexFunctionParams = new Regex(@"\(?(.+)\)?", RegexOptions.Compiled); |
|
private static readonly Regex regexDoubleColon = new Regex(@"\w+::", RegexOptions.Compiled); |
|
private static readonly Regex regexName = new Regex(@"(\w+)", RegexOptions.Compiled); |
|
|
|
private readonly Dictionary<DeclarationContext, List<Function>> overloads = |
|
new Dictionary<DeclarationContext, List<Function>>(); |
|
|
|
public HandleDefaultParamValuesPass() |
|
{ |
|
VisitOptions.VisitFunctionParameters = false; |
|
} |
|
|
|
public override bool VisitTranslationUnit(TranslationUnit unit) |
|
{ |
|
if (!unit.IsGenerated) |
|
return false; |
|
var result = base.VisitTranslationUnit(unit); |
|
foreach (var overload in overloads) |
|
overload.Key.Functions.AddRange(overload.Value); |
|
overloads.Clear(); |
|
return result; |
|
} |
|
|
|
public override bool VisitFunctionDecl(Function function) |
|
{ |
|
if (!base.VisitFunctionDecl(function) || function.Ignore) |
|
return false; |
|
|
|
var overloadIndices = new List<int>(function.Parameters.Count); |
|
foreach (var parameter in function.Parameters.Where(p => p.DefaultArgument != null)) |
|
{ |
|
var result = parameter.DefaultArgument.String; |
|
if (PrintExpression(function, parameter.Type, |
|
parameter.OriginalDefaultArgument, ref result) == null) |
|
overloadIndices.Add(function.Parameters.IndexOf(parameter)); |
|
if (string.IsNullOrEmpty(result)) |
|
{ |
|
parameter.DefaultArgument = null; |
|
foreach (var p in function.Parameters.TakeWhile(p => p != parameter)) |
|
p.DefaultArgument = null; |
|
} |
|
else |
|
parameter.DefaultArgument.String = result; |
|
} |
|
|
|
GenerateOverloads(function, overloadIndices); |
|
|
|
return true; |
|
} |
|
|
|
private bool? PrintExpression(Function function, Type type, Expression expression, ref string result) |
|
{ |
|
var desugared = type.Desugar(); |
|
|
|
if (!desugared.IsPrimitiveTypeConvertibleToRef() && |
|
expression.String == "0") |
|
{ |
|
result = $"default({desugared})"; |
|
return true; |
|
} |
|
|
|
// constants are obtained through dynamic calls at present so they are not compile-time values in target languages |
|
if (expression.Declaration is Variable || |
|
(!Options.MarshalCharAsManagedChar && |
|
desugared.IsPrimitiveType(PrimitiveType.UChar))) |
|
return null; |
|
|
|
if (desugared.IsPrimitiveTypeConvertibleToRef()) |
|
{ |
|
var method = function as Method; |
|
if (method != null && method.IsConstructor) |
|
{ |
|
result = string.Empty; |
|
return false; |
|
} |
|
return null; |
|
} |
|
|
|
if (CheckForDefaultPointer(desugared, ref result)) |
|
return true; |
|
|
|
if (expression.Class == StatementClass.Call) |
|
{ |
|
if (expression.Declaration.Ignore) |
|
{ |
|
result = null; |
|
return false; |
|
} |
|
return null; |
|
} |
|
|
|
var defaultConstruct = CheckForDefaultConstruct(desugared, expression, ref result); |
|
if (defaultConstruct != false) |
|
return defaultConstruct; |
|
|
|
return CheckForSimpleExpressions(expression, ref result, desugared); |
|
} |
|
|
|
private bool CheckForSimpleExpressions(Expression expression, ref string result, Type desugared) |
|
{ |
|
return CheckFloatSyntax(desugared, expression, ref result) || |
|
CheckForEnumValue(desugared, expression, ref result) || |
|
CheckForDefaultChar(desugared, ref result); |
|
} |
|
|
|
private bool CheckForDefaultPointer(Type desugared, ref string result) |
|
{ |
|
if (!desugared.IsPointer()) |
|
return false; |
|
|
|
// IntPtr.Zero is not a constant |
|
if (desugared.IsPointerToPrimitiveType(PrimitiveType.Void)) |
|
{ |
|
result = "new global::System.IntPtr()"; |
|
return true; |
|
} |
|
|
|
if (desugared.IsPrimitiveTypeConvertibleToRef()) |
|
return false; |
|
|
|
Class @class; |
|
if (desugared.GetFinalPointee().TryGetClass(out @class) && @class.IsValueType) |
|
{ |
|
result = string.Format("new {0}()", |
|
new CSharpTypePrinter(Context).VisitClassDecl(@class)); |
|
return true; |
|
} |
|
|
|
result = "null"; |
|
return true; |
|
} |
|
|
|
private bool? CheckForDefaultConstruct(Type desugared, Expression expression, |
|
ref string result) |
|
{ |
|
var type = desugared.GetFinalPointee() ?? desugared; |
|
|
|
Class decl; |
|
if (!type.TryGetClass(out decl)) |
|
return false; |
|
|
|
var ctor = expression as CXXConstructExpr; |
|
|
|
var typePrinter = new CSharpTypePrinter(Context); |
|
typePrinter.PushMarshalKind(MarshalKind.DefaultExpression); |
|
|
|
var typePrinterResult = type.Visit(typePrinter).Type; |
|
|
|
TypeMap typeMap; |
|
if (TypeMaps.FindTypeMap(decl, type, out typeMap)) |
|
{ |
|
var typePrinterContext = new TypePrinterContext() |
|
{ |
|
Kind = typePrinter.Kind, |
|
MarshalKind = typePrinter.MarshalKind, |
|
Type = type |
|
}; |
|
|
|
var typeInSignature = typeMap.CSharpSignatureType(typePrinterContext) |
|
.SkipPointerRefs().Desugar(); |
|
|
|
Enumeration @enum; |
|
if (typeInSignature.TryGetEnum(out @enum)) |
|
{ |
|
if (ctor != null && |
|
(ctor.Arguments.Count == 0 || |
|
HasSingleZeroArgExpression((Function) ctor.Declaration))) |
|
{ |
|
result = "0"; |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
if (ctor != null && typePrinterResult == "string" && ctor.Arguments.Count == 0) |
|
{ |
|
result = "\"\""; |
|
return true; |
|
} |
|
} |
|
|
|
if (ctor == null) |
|
{ |
|
CheckForSimpleExpressions(expression, ref result, desugared); |
|
return decl.IsValueType ? (bool?) false : null; |
|
} |
|
|
|
var method = (Method) expression.Declaration; |
|
var expressionSupported = decl.IsValueType && method.Parameters.Count == 0; |
|
|
|
if (expression.String.Contains('(')) |
|
{ |
|
var argsBuilder = new StringBuilder("new "); |
|
argsBuilder.Append(typePrinterResult); |
|
argsBuilder.Append('('); |
|
for (var i = 0; i < ctor.Arguments.Count; i++) |
|
{ |
|
var argument = ctor.Arguments[i]; |
|
var argResult = argument.String; |
|
expressionSupported &= PrintExpression(method, |
|
method.Parameters[i].Type.Desugar(), argument, ref argResult) ?? false; |
|
argsBuilder.Append(argResult); |
|
if (i < ctor.Arguments.Count - 1) |
|
argsBuilder.Append(", "); |
|
} |
|
argsBuilder.Append(')'); |
|
result = argsBuilder.ToString(); |
|
} |
|
return expressionSupported ? true : (bool?) null; |
|
} |
|
|
|
private static bool CheckFloatSyntax(Type desugared, Statement statement, ref string result) |
|
{ |
|
var builtin = desugared as BuiltinType; |
|
if (builtin != null) |
|
{ |
|
switch (builtin.Type) |
|
{ |
|
case PrimitiveType.Float: |
|
if (statement.String.EndsWith(".F", System.StringComparison.Ordinal)) |
|
{ |
|
result = statement.String.Replace(".F", ".0F"); |
|
return true; |
|
} |
|
break; |
|
case PrimitiveType.Double: |
|
if (statement.String.EndsWith(".", System.StringComparison.Ordinal)) |
|
{ |
|
result = statement.String + '0'; |
|
return true; |
|
} |
|
break; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
private bool CheckForEnumValue(Type desugared, Statement statement, |
|
ref string result) |
|
{ |
|
var enumItem = statement.Declaration as Enumeration.Item; |
|
if (enumItem != null) |
|
return true; |
|
|
|
var call = statement.Declaration as Function; |
|
if (call != null && statement.String != "0") |
|
{ |
|
var @params = regexFunctionParams.Match(statement.String).Groups[1].Value; |
|
result = TranslateEnumExpression(call, desugared, @params); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
private string TranslateEnumExpression(Function function, |
|
Type desugared, string @params) |
|
{ |
|
if (@params.Contains("::")) |
|
return regexDoubleColon.Replace(@params, desugared + "."); |
|
|
|
return regexName.Replace(@params, desugared + ".$1"); |
|
} |
|
|
|
private static bool HasSingleZeroArgExpression(Function function) |
|
{ |
|
if (function.Parameters.Count != 1) |
|
return false; |
|
|
|
var defaultArgument = function.Parameters[0].OriginalDefaultArgument; |
|
return defaultArgument is BuiltinTypeExpression && |
|
((BuiltinTypeExpression) defaultArgument).Value == 0; |
|
} |
|
|
|
private bool CheckForDefaultChar(Type desugared, ref string result) |
|
{ |
|
int value; |
|
if (int.TryParse(result, out value) && |
|
((Options.MarshalCharAsManagedChar && |
|
desugared.IsPrimitiveType(PrimitiveType.Char)) || |
|
desugared.IsPrimitiveType(PrimitiveType.WideChar))) |
|
{ |
|
result = value == 0 ? "'\\0'" : ("(char) " + result); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
private void GenerateOverloads(Function function, List<int> overloadIndices) |
|
{ |
|
foreach (var overloadIndex in overloadIndices) |
|
{ |
|
var method = function as Method; |
|
Function overload = method != null ? new Method(method) : new Function(function); |
|
overload.OriginalFunction = function; |
|
overload.SynthKind = FunctionSynthKind.DefaultValueOverload; |
|
for (int i = overloadIndex; i < function.Parameters.Count; ++i) |
|
overload.Parameters[i].GenerationKind = GenerationKind.None; |
|
|
|
var indices = overloadIndices.Where(i => i < overloadIndex).ToList(); |
|
if (indices.Any()) |
|
for (int i = 0; i <= indices.Last(); i++) |
|
if (i != overloadIndex) |
|
overload.Parameters[i].DefaultArgument = null; |
|
|
|
if (method != null) |
|
((Class) function.Namespace).Methods.Add((Method) overload); |
|
else |
|
{ |
|
List<Function> functions; |
|
if (overloads.ContainsKey(function.Namespace)) |
|
functions = overloads[function.Namespace]; |
|
else |
|
overloads.Add(function.Namespace, functions = new List<Function>()); |
|
functions.Add(overload); |
|
} |
|
|
|
for (int i = 0; i <= overloadIndex; i++) |
|
function.Parameters[i].DefaultArgument = null; |
|
} |
|
} |
|
} |
|
}
|
|
|