Skip to content

Commit 3aec24c

Browse files
committed
InteropGen: append hash to end of function names based on signature, allows us to support overloaded methods, multiple ctors, etc.
1 parent 65d1217 commit 3aec24c

File tree

6 files changed

+76
-16
lines changed

6 files changed

+76
-16
lines changed

Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class c )
102102
var method = c.Methods[i];
103103
var returnType = Utils.GetManagedType( method.ReturnType );
104104
var name = method.Name;
105+
var hash = method.Hash;
105106

106107
var returnsPointer = Utils.IsPointer( method.ReturnType ) && !method.IsConstructor && !method.IsDestructor;
107108

@@ -129,7 +130,7 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class c )
129130
// any parameters. The return type is the last type argument passed to
130131
// the delegate.
131132
//
132-
decls[i] = $"private static {delegateSignature} _{name} = ({delegateSignature})Mocha.Common.Global.UnmanagedArgs.__{c.Name}_{name}MethodPtr;";
133+
decls[i] = $"private static {delegateSignature} _{hash} = ({delegateSignature})Mocha.Common.Global.UnmanagedArgs.__{c.Name}_{hash};";
133134
}
134135

135136
//
@@ -148,17 +149,16 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class c )
148149
writer.WriteLine();
149150

150151
// Ctor
151-
if ( c.Methods.Any( x => x.IsConstructor ) )
152+
foreach ( var ctor in c.Methods.Where( x => x.IsConstructor ) )
152153
{
153-
var ctor = c.Methods.First( x => x.IsConstructor );
154154
var managedCtorArgs = string.Join( ", ", ctor.Parameters.Select( x => $"{Utils.GetManagedType( x.Type )} {x.Name}" ) );
155155

156156
writer.WriteLine( $"public {c.Name}( {managedCtorArgs} )" );
157157
writer.WriteLine( "{" );
158158
writer.Indent++;
159159

160160
var ctorCallArgs = string.Join( ", ", ctor.Parameters.Select( x => x.Name ) );
161-
writer.WriteLine( $"this.NativePtr = this.Ctor( {ctorCallArgs} );" );
161+
writer.WriteLine( $"this.NativePtr = this.{ctor.Name}( {ctorCallArgs} );" );
162162

163163
writer.Indent--;
164164
writer.WriteLine( "}" );
@@ -175,6 +175,7 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class c )
175175
// Call parameters as comma-separated string
176176
var managedCallParams = string.Join( ", ", method.Parameters.Select( x => $"{Utils.GetManagedType( x.Type )} {x.Name}" ) );
177177
var name = method.Name;
178+
var hash = method.Hash;
178179

179180
// We return a pointer to the created object if it's a ctor/dtor, but otherwise we'll do auto-conversions to our managed types
180181
var returnType = (method.IsConstructor || method.IsDestructor) ? "IntPtr" : Utils.GetManagedType( method.ReturnType );
@@ -188,12 +189,12 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class c )
188189
accessLevel += " static";
189190

190191
// Write function signature
191-
writer.WriteLine( $"{accessLevel} {returnType} {name}( {managedCallParams} ) " );
192+
writer.WriteLine( $"{accessLevel} {returnType} {method.Name}( {managedCallParams} ) " );
192193
writer.WriteLine( "{" );
193194
writer.Indent++;
194195

195196
// Spin up a MemoryContext instance
196-
writer.WriteLine( $"using var ctx = new MemoryContext( \"{c.Name}.{name}\" );" );
197+
writer.WriteLine( $"using var ctx = new MemoryContext( \"{c.Name}.{method.Name}\" );" );
197198

198199
//
199200
// Gather function body
@@ -213,7 +214,7 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class c )
213214
if ( returnsPointer )
214215
{
215216
// If we want to return a pointer:
216-
writer.WriteLine( $"var ptr = _{name}( {functionCallArgs} );" );
217+
writer.WriteLine( $"var ptr = _{hash}( {functionCallArgs} );" );
217218
writer.WriteLine( $"var obj = FormatterServices.GetUninitializedObject( typeof( {returnType} ) ) as {returnType};" );
218219
writer.WriteLine( $"obj.NativePtr = ptr;" );
219220
writer.WriteLine( $"return obj;" );
@@ -229,7 +230,7 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class c )
229230
writer.Write( "ctx.GetString( " );
230231

231232
// Call the function..
232-
writer.Write( $"_{name}( {functionCallArgs} )" );
233+
writer.Write( $"_{hash}( {functionCallArgs} )" );
233234

234235
// Finish string
235236
if ( returnType == "string" )
@@ -300,7 +301,7 @@ private static void GenerateNamespaceCode( IndentedTextWriter writer, Namespace
300301
// any parameters. The return type is the last type argument passed to
301302
// the delegate.
302303
//
303-
decls[i] = $"private static {delegateSignature} _{name} = ({delegateSignature})Mocha.Common.Global.UnmanagedArgs.__{ns.Name}_{name}MethodPtr;";
304+
decls[i] = $"private static {delegateSignature} _{name} = ({delegateSignature})Mocha.Common.Global.UnmanagedArgs.__{ns.Name}_{method.Hash};";
304305
}
305306

306307
//

Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class c )
7979
return $"{x.Type} {x.Name}";
8080
} ) );
8181

82-
var signature = $"extern \"C\" inline {method.ReturnType} __{c.Name}_{method.Name}( {argStr} )";
82+
var signature = $"extern \"C\" inline {method.ReturnType} __{c.Name}_{method.Hash}( {argStr} )";
8383
var body = "";
8484
var parameters = string.Join( ", ", method.Parameters.Select( x => x.Name ) );
8585

@@ -129,7 +129,7 @@ private static void GenerateNamespaceCode( IndentedTextWriter writer, Namespace
129129
return $"{x.Type} {x.Name}";
130130
} ) );
131131

132-
var signature = $"extern \"C\" inline {method.ReturnType} __{ns.Name}_{method.Name}( {argStr} )";
132+
var signature = $"extern \"C\" inline {method.ReturnType} __{ns.Name}_{method.Hash}( {argStr} )";
133133
var body = "";
134134
var parameters = string.Join( ", ", method.Parameters.Select( x => x.Name ) );
135135

Source/MochaTool.InteropGen/Parsing/ContainerBuilder.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ internal sealed class ContainerBuilder
1111
/// The type of container that is being built.
1212
/// </summary>
1313
internal ContainerType Type { get; }
14+
1415
/// <summary>
1516
/// The name of the container.
1617
/// </summary>
@@ -24,6 +25,29 @@ internal sealed class ContainerBuilder
2425
private readonly ImmutableArray<Variable>.Builder fields;
2526
private readonly ImmutableArray<Method>.Builder methods;
2627

28+
/// <summary>
29+
/// Does a method with this name already exist in this container?
30+
/// </summary>
31+
public bool HasMethod( string name ) => methods.Any( x => x.Name == name );
32+
33+
/// <summary>
34+
/// Finds an available name for a function. If one isn't available, follows
35+
/// the function name up with a counter.
36+
/// </summary>
37+
public string FindFreeName( string desiredName )
38+
{
39+
var name = desiredName;
40+
var index = 0;
41+
42+
while ( HasMethod( name ) )
43+
{
44+
index++;
45+
name = $"{desiredName}{index}";
46+
}
47+
48+
return desiredName;
49+
}
50+
2751
/// <summary>
2852
/// Initializes a new instance of <see cref="ContainerBuilder"/>.
2953
/// </summary>

Source/MochaTool.InteropGen/Parsing/Method.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ internal sealed class Method : IUnit
1111
/// The name of the method.
1212
/// </summary>
1313
public string Name { get; }
14+
1415
/// <summary>
1516
/// The literal string containing the return type of the method.
1617
/// </summary>
@@ -20,10 +21,12 @@ internal sealed class Method : IUnit
2021
/// Whether or not the method is a constructor.
2122
/// </summary>
2223
internal bool IsConstructor { get; } = false;
24+
2325
/// <summary>
2426
/// Whether or not the method is a destructor.
2527
/// </summary>
2628
internal bool IsDestructor { get; } = false;
29+
2730
/// <summary>
2831
/// Whether or not the method is static.
2932
/// </summary>
@@ -34,6 +37,11 @@ internal sealed class Method : IUnit
3437
/// </summary>
3538
internal ImmutableArray<Variable> Parameters { get; }
3639

40+
/// <summary>
41+
/// A hash representing the signature for this method.
42+
/// </summary>
43+
public string Hash { get; }
44+
3745
/// <summary>
3846
/// Initializes a new instance of <see cref="Method"/>.
3947
/// </summary>
@@ -53,6 +61,33 @@ private Method( string name, string returnType, bool isConstructor, bool isDestr
5361
IsStatic = isStatic;
5462

5563
Parameters = parameters;
64+
65+
Hash = $"{Name}{ComputeHash()}";
66+
}
67+
68+
private string ComputeHash()
69+
{
70+
//
71+
// alex: We're taking an MD5 representation of the function signature and then truncating
72+
// it to 8 characters. Our chances of a collision are "high" (~1 in 4,294,967,296) compared
73+
// to other crypto-secure representations (e.g. SHA) but we're not too bothered about that
74+
// here. Chances are we'll have 2 or 3 functions that share a name (thru method overloading).
75+
//
76+
77+
var returnType = ReturnType;
78+
var name = Name;
79+
var parameters = string.Join( ",", Parameters.Select( x => $"{x.Type}{x.Name}" ) );
80+
81+
var signature = $"{returnType}{name}{parameters}";
82+
83+
// Use input string to calculate MD5 hash
84+
using ( System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create() )
85+
{
86+
byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes( signature );
87+
byte[] hashBytes = md5.ComputeHash( inputBytes );
88+
89+
return Convert.ToHexString( hashBytes )[..8];
90+
}
5691
}
5792

5893
/// <summary>

Source/MochaTool.InteropGen/Parsing/Parser.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ private static unsafe CXChildVisitResult VisitMethod( in CXCursor cursor, Contai
135135
// We're traversing a constructor.
136136
if ( cursor.Kind == CXCursorKind.CXCursor_Constructor )
137137
{
138-
name = "Ctor";
138+
name = currentContainer.FindFreeName( "Ctor" );
139139
returnType = currentContainer.Name + '*';
140140
isStatic = false;
141141
isConstructor = true;
@@ -144,7 +144,7 @@ private static unsafe CXChildVisitResult VisitMethod( in CXCursor cursor, Contai
144144
// We're traversing a destructor.
145145
else if ( cursor.Kind == CXCursorKind.CXCursor_Destructor )
146146
{
147-
name = "DeCtor";
147+
name = currentContainer.FindFreeName( "Dtor" );
148148
returnType = '~' + currentContainer.Name;
149149
isStatic = false;
150150
isConstructor = false;

Source/MochaTool.InteropGen/Program.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ private static async Task WriteManagedStructAsync( string baseDir, IEnumerable<(
119119

120120
managedStructWriter.WriteLine( "public IntPtr __Root;" );
121121

122-
var managedStructBody = string.Join( "\r\n\t", methods.Select( x => $"public IntPtr __{x.Name}_{x.method.Name}MethodPtr;" ) );
122+
var managedStructBody = string.Join( "\r\n\t", methods.Select( x => $"public IntPtr __{x.Name}_{x.method.Hash};" ) );
123123
managedStructWriter.Write( managedStructBody );
124124
managedStructWriter.WriteLine();
125125

@@ -151,7 +151,7 @@ private static async Task WriteNativeStructAsync( string baseDir, IEnumerable<(s
151151

152152
nativeStructWriter.WriteLine( "void* __Root;" );
153153

154-
var nativeStructBody = string.Join( "\r\n\t", methods.Select( x => $"void* __{x.Name}_{x.method.Name}MethodPtr;" ) );
154+
var nativeStructBody = string.Join( "\r\n\t", methods.Select( x => $"void* __{x.Name}_{x.method.Hash};" ) );
155155
nativeStructWriter.Write( nativeStructBody );
156156
nativeStructWriter.WriteLine();
157157

@@ -165,7 +165,7 @@ private static async Task WriteNativeStructAsync( string baseDir, IEnumerable<(s
165165

166166
nativeStructWriter.WriteLine( "Root::GetInstance()," );
167167

168-
nativeStructBody = string.Join( ",\r\n\t", methods.Select( x => $"(void*)__{x.Name}_{x.method.Name}" ) );
168+
nativeStructBody = string.Join( ",\r\n\t", methods.Select( x => $"(void*)__{x.Name}_{x.method.Hash}" ) );
169169
nativeStructWriter.Write( nativeStructBody );
170170
nativeStructWriter.WriteLine();
171171

0 commit comments

Comments
 (0)