mirror of https://github.com/mono/CppSharp.git
3 changed files with 1 additions and 243 deletions
@ -1,227 +0,0 @@
@@ -1,227 +0,0 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using CppSharp.AST; |
||||
using CppSharp.Generators; |
||||
using CppSharp.Generators.CLI; |
||||
using CppSharp.Passes; |
||||
|
||||
namespace CppSharp |
||||
{ |
||||
// This pass adds Equals and GetHashCode methods to classes.
|
||||
// It will also add a ToString method if the insertion operator (<<)
|
||||
// of the class is overloaded.
|
||||
// Note that the OperatorToClassPass needs to run first in order for
|
||||
// this to work.
|
||||
public class ObjectOverridesPass : TranslationUnitPass |
||||
{ |
||||
private Generator Generator { get; set; } |
||||
|
||||
public ObjectOverridesPass(Generator generator) |
||||
{ |
||||
Generator = generator; |
||||
} |
||||
|
||||
private bool needsStreamInclude; |
||||
private void OnUnitGenerated(GeneratorOutput output) |
||||
{ |
||||
needsStreamInclude = false; |
||||
foreach (var template in output.Outputs) |
||||
{ |
||||
foreach (var block in template.FindBlocks(BlockKind.MethodBody)) |
||||
{ |
||||
var method = block.Object as Method; |
||||
VisitMethod(method, block); |
||||
} |
||||
if (needsStreamInclude) |
||||
{ |
||||
var sourcesTemplate = template as CLISources; |
||||
if (sourcesTemplate != null) |
||||
{ |
||||
foreach (var block in sourcesTemplate.FindBlocks(BlockKind.Includes)) |
||||
{ |
||||
block.WriteLine("#include <sstream>"); |
||||
block.WriteLine(""); |
||||
break; |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void VisitMethod(Method method, Block block) |
||||
{ |
||||
if (!method.IsSynthetized) |
||||
return; |
||||
|
||||
var @class = (Class)method.Namespace; |
||||
|
||||
if (method.Name == "GetHashCode" && method.Parameters.Count == 0) |
||||
GenerateGetHashCode(block); |
||||
|
||||
if (method.Name == "Equals" && method.Parameters.Count == 1) |
||||
GenerateEquals(@class, block, method); |
||||
|
||||
if (method.Name == "ToString" && method.Parameters.Count == 0) |
||||
GenerateToString(block); |
||||
} |
||||
|
||||
void GenerateGetHashCode(Block block) |
||||
{ |
||||
block.Write("return (int)NativePtr;"); |
||||
} |
||||
|
||||
void GenerateEquals(Class @class, Block block, Method method) |
||||
{ |
||||
var cliTypePrinter = new CLITypePrinter(Context); |
||||
var classCliType = @class.Visit(cliTypePrinter); |
||||
|
||||
block.WriteLine("if (!object) return false;"); |
||||
block.WriteLine("auto obj = dynamic_cast<{0}>({1});", |
||||
classCliType, method.Parameters[0].Name); |
||||
block.NewLine(); |
||||
|
||||
block.WriteLine("if (!obj) return false;"); |
||||
block.Write("return __Instance == obj->__Instance;"); |
||||
} |
||||
|
||||
void GenerateToString(Block block) |
||||
{ |
||||
needsStreamInclude = true; |
||||
block.WriteLine("std::ostringstream os;"); |
||||
block.WriteLine("os << *NativePtr;"); |
||||
block.Write("return clix::marshalString<clix::E_UTF8>(os.str());"); |
||||
} |
||||
|
||||
private bool isHooked; |
||||
public override bool VisitClassDecl(Class @class) |
||||
{ |
||||
// FIXME: Add a better way to hook the event
|
||||
if (!isHooked) |
||||
{ |
||||
Generator.OnUnitGenerated += OnUnitGenerated; |
||||
isHooked = true; |
||||
} |
||||
|
||||
if (!VisitDeclaration(@class)) |
||||
return false; |
||||
|
||||
// We can't handle value types yet
|
||||
// The generated code assumes that a NativePtr is available
|
||||
if (@class.IsValueType) |
||||
return false; |
||||
|
||||
foreach (var method in @class.Methods) |
||||
{ |
||||
if (!IsInsertionOperator(method)) |
||||
continue; |
||||
|
||||
// Create the ToString method
|
||||
var stringType = GetType("std::string"); |
||||
if (stringType == null) |
||||
stringType = new CILType(typeof(string)); |
||||
var toStringMethod = new Method() |
||||
{ |
||||
Name = "ToString", |
||||
Namespace = @class, |
||||
ReturnType = new QualifiedType(stringType), |
||||
SynthKind = FunctionSynthKind.ComplementOperator, |
||||
IsProxy = true |
||||
}; |
||||
toStringMethod.OverriddenMethods.Add(new Method { Name = "ToString" }); |
||||
@class.Methods.Add(toStringMethod); |
||||
|
||||
Diagnostics.Debug("Function converted to ToString: {0}::{1}", |
||||
@class.Name, method.Name); |
||||
|
||||
break; |
||||
} |
||||
|
||||
var methodEqualsParam = new Parameter |
||||
{ |
||||
Name = "object", |
||||
QualifiedType = new QualifiedType(new CILType(typeof(Object))) |
||||
}; |
||||
|
||||
var methodEquals = new Method |
||||
{ |
||||
Name = "Equals", |
||||
Namespace = @class, |
||||
ReturnType = new QualifiedType(new BuiltinType(PrimitiveType.Bool)), |
||||
SynthKind = FunctionSynthKind.ComplementOperator, |
||||
IsProxy = true |
||||
}; |
||||
|
||||
methodEqualsParam.Namespace = methodEquals; |
||||
methodEquals.Parameters.Add(methodEqualsParam); |
||||
|
||||
methodEquals.OverriddenMethods.Add(new Method { Name = "Equals" }); |
||||
@class.Methods.Add(methodEquals); |
||||
|
||||
var methodHashCode = new Method |
||||
{ |
||||
Name = "GetHashCode", |
||||
Namespace = @class, |
||||
ReturnType = new QualifiedType(new BuiltinType(PrimitiveType.Int)), |
||||
SynthKind = FunctionSynthKind.ComplementOperator, |
||||
IsProxy = true |
||||
}; |
||||
methodHashCode.OverriddenMethods.Add(new Method { Name = "GetHashCode" }); |
||||
|
||||
@class.Methods.Add(methodHashCode); |
||||
return true; |
||||
} |
||||
|
||||
private Dictionary<string, AST.Type> typeCache; |
||||
protected AST.Type GetType(string typeName) |
||||
{ |
||||
if (typeCache == null) |
||||
typeCache = new Dictionary<string, AST.Type>(); |
||||
AST.Type result; |
||||
if (!typeCache.TryGetValue(typeName, out result)) |
||||
{ |
||||
var typeDef = ASTContext.FindTypedef(typeName) |
||||
.FirstOrDefault(); |
||||
if (typeDef != null) |
||||
result = new TypedefType() { Declaration = typeDef }; |
||||
typeCache.Add(typeName, result); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
private bool IsInsertionOperator(Method method) |
||||
{ |
||||
// Do some basic check
|
||||
if (!method.IsOperator) |
||||
return false; |
||||
if (method.OperatorKind != CXXOperatorKind.LessLess) |
||||
return false; |
||||
if (method.Parameters.Count != 2) |
||||
return false; |
||||
|
||||
// Check that first parameter is a std::ostream&
|
||||
var fstType = method.Parameters[0].Type as PointerType; |
||||
if (fstType == null) |
||||
return false; |
||||
var oStreamType = GetType("std::ostream"); |
||||
if (oStreamType == null) |
||||
return false; |
||||
if (!oStreamType.Equals(fstType.Pointee)) |
||||
return false; |
||||
|
||||
// Check that second parameter is a const CLASS&
|
||||
var sndType = method.Parameters[1].Type as PointerType; |
||||
if (sndType == null) |
||||
return false; |
||||
if (!sndType.QualifiedPointee.Qualifiers.IsConst) |
||||
return false; |
||||
var @class = method.Namespace as Class; |
||||
var classType = new TagType(@class); |
||||
if (!classType.Equals(sndType.Pointee)) |
||||
return false; |
||||
|
||||
return true; |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue