1
1
using Biohazrd . CSharp . Metadata ;
2
+ using ClangSharp . Pathogen ;
2
3
using System ;
3
4
using System . Diagnostics ;
4
5
using System . Runtime . CompilerServices ;
@@ -10,16 +11,20 @@ partial class CSharpLibraryGenerator
10
11
{
11
12
private ref struct EmitFunctionContext
12
13
{
14
+ public bool NeedsTrampoline { get ; }
13
15
public string DllImportName { get ; }
14
16
public TypeReference ? ThisType { get ; }
15
17
public string ThisParameterName => "this" ;
16
18
public string ReturnBufferParameterName => "__returnBuffer" ;
17
19
18
20
public EmitFunctionContext ( VisitorContext context , TranslatedFunction declaration )
19
21
{
20
- // When this function is an instance method, we add a suffix to the P/Invoke method to ensure they don't conflict with other methods.
22
+ // We emit a trampoline for functions which are instance methods or return via reference to hide those ABI semantics
23
+ NeedsTrampoline = declaration . IsInstanceMethod || declaration . ReturnByReference ;
24
+
25
+ // When this function is uses a trampoline, we add a suffix to the P/Invoke method to ensure they don't conflict with other methods.
21
26
// (For instance, when there's a SomeClass::Method() method in addition to a SomeClass::Method(SomeClass*) method.)
22
- DllImportName = declaration . IsInstanceMethod ? $ "{ declaration . Name } _PInvoke" : declaration . Name ;
27
+ DllImportName = NeedsTrampoline ? $ "{ declaration . Name } _PInvoke" : declaration . Name ;
23
28
24
29
// ThisType is the type of `this` for instance methods.
25
30
if ( ! declaration . IsInstanceMethod )
@@ -43,7 +48,7 @@ protected override void VisitFunction(VisitorContext context, TranslatedFunction
43
48
{ EmitFunctionDllImport ( context , emitContext , declaration ) ; }
44
49
45
50
// Emit the trampoline
46
- if ( declaration . IsInstanceMethod )
51
+ if ( emitContext . NeedsTrampoline )
47
52
{ EmitFunctionTrampoline ( context , emitContext , declaration ) ; }
48
53
}
49
54
@@ -90,8 +95,8 @@ private void EmitFunctionDllImport(VisitorContext context, EmitFunctionContext e
90
95
{ Writer . WriteLine ( "[return: MarshalAs(UnmanagedType.I1)]" ) ; }
91
96
92
97
// Write out the function signature
93
- // Instance methods are accessed via trampoline, so we translate the DllImport as private.
94
- AccessModifier accessibility = declaration . IsInstanceMethod ? AccessModifier . Private : declaration . Accessibility ;
98
+ // The P/Invokes for functions accessed via trampoline are emitted as private.
99
+ AccessModifier accessibility = emitContext . NeedsTrampoline ? AccessModifier . Private : declaration . Accessibility ;
95
100
Writer . Write ( $ "{ accessibility . ToCSharpKeyword ( ) } static extern ") ;
96
101
97
102
// If the return value is passed by reference, the return type is the return buffer pointer
@@ -107,7 +112,7 @@ private void EmitFunctionDllImport(VisitorContext context, EmitFunctionContext e
107
112
108
113
private void EmitFunctionTrampoline ( VisitorContext context , EmitFunctionContext emitContext , TranslatedFunction declaration )
109
114
{
110
- if ( emitContext . ThisType is null )
115
+ if ( ! emitContext . NeedsTrampoline )
111
116
{ throw new ArgumentException ( "A function trampoline is not valid in this context." , nameof ( emitContext ) ) ; }
112
117
113
118
Writer . EnsureSeparation ( ) ;
@@ -184,6 +189,8 @@ private void EmitFunctionTrampoline(VisitorContext context, EmitFunctionContext
184
189
185
190
// Emit the method signature
186
191
Writer . Write ( $ "{ declaration . Accessibility . ToCSharpKeyword ( ) } unsafe ") ;
192
+ if ( ! declaration . IsInstanceMethod )
193
+ { Writer . Write ( "static " ) ; }
187
194
WriteType ( context , declaration , declaration . ReturnType ) ;
188
195
Writer . Write ( $ " { SanitizeIdentifier ( declaration . Name ) } (") ;
189
196
EmitFunctionParameterList ( context , emitContext , declaration , EmitParameterListMode . TrampolineParameters ) ;
@@ -204,15 +211,27 @@ private void EmitFunctionTrampoline(VisitorContext context, EmitFunctionContext
204
211
// Emit the dispatch
205
212
using ( Writer . Block ( ) )
206
213
{
207
- Writer . Write ( $ "fixed (") ;
208
- WriteType ( context , declaration , emitContext . ThisType ) ;
209
- Writer . WriteLine ( $ " { SanitizeIdentifier ( emitContext . ThisParameterName ) } = &this)") ;
214
+ bool hasThis ;
215
+ if ( emitContext . ThisType is not null )
216
+ {
217
+ hasThis = true ;
218
+ Debug . Assert ( declaration . IsInstanceMethod ) ;
219
+
220
+ Writer . Write ( $ "fixed (") ;
221
+ WriteType ( context , declaration , emitContext . ThisType ! ) ;
222
+ Writer . WriteLine ( $ " { SanitizeIdentifier ( emitContext . ThisParameterName ) } = &this)") ;
223
+ }
224
+ else
225
+ {
226
+ hasThis = false ;
227
+ Debug . Assert ( ! declaration . IsInstanceMethod ) ;
228
+ }
210
229
211
230
bool hasReturnValue = declaration . ReturnType is not VoidTypeReference ;
212
231
213
232
if ( hasReturnValue && declaration . ReturnByReference )
214
233
{
215
- using ( Writer . Block ( ) )
234
+ void EmitFunctionBodyWithReturnByReference ( EmitFunctionContext emitContext )
216
235
{
217
236
WriteType ( context , declaration , declaration . ReturnType ) ;
218
237
Writer . WriteLine ( $ " { SanitizeIdentifier ( emitContext . ReturnBufferParameterName ) } ;") ;
@@ -223,10 +242,21 @@ private void EmitFunctionTrampoline(VisitorContext context, EmitFunctionContext
223
242
224
243
Writer . WriteLine ( $ "return { SanitizeIdentifier ( emitContext . ReturnBufferParameterName ) } ;") ;
225
244
}
245
+
246
+ if ( hasThis )
247
+ {
248
+ // If we have a fixed statement for the this pointer, wrap the return buffer logic with a block
249
+ using ( Writer . Block ( ) )
250
+ { EmitFunctionBodyWithReturnByReference ( emitContext ) ; }
251
+ }
252
+ else
253
+ { EmitFunctionBodyWithReturnByReference ( emitContext ) ; }
226
254
}
227
255
else
228
256
{
229
- Writer . Write ( "{ " ) ;
257
+ // If we have a fixed statement for the this pointer, write out the curly braces for it
258
+ if ( hasThis )
259
+ { Writer . Write ( "{ " ) ; }
230
260
231
261
if ( hasReturnValue )
232
262
{ Writer . Write ( "return " ) ; }
@@ -235,7 +265,8 @@ private void EmitFunctionTrampoline(VisitorContext context, EmitFunctionContext
235
265
EmitFunctionParameterList ( context , emitContext , declaration , EmitParameterListMode . TrampolineArguments , thisTypeCast ) ;
236
266
Writer . Write ( ");" ) ;
237
267
238
- Writer . WriteLine ( " }" ) ;
268
+ if ( hasThis )
269
+ { Writer . WriteLine ( " }" ) ; }
239
270
}
240
271
}
241
272
}
@@ -268,45 +299,55 @@ private void EmitFunctionParameterList(VisitorContext context, EmitFunctionConte
268
299
// Write out the this/retbuf parameters
269
300
if ( writeImplicitParameters )
270
301
{
271
- // Write out the this pointer
272
- if ( emitContext . ThisType is not null )
302
+ void WriteOutReturnBuffer ( EmitFunctionContext emitContext )
273
303
{
304
+ if ( ! first )
305
+ { Writer . Write ( ", " ) ; }
306
+
307
+ if ( ! declaration . IsVirtual )
308
+ { Writer . Write ( "out " ) ; }
309
+ else if ( mode == EmitParameterListMode . TrampolineArguments )
310
+ { Writer . Write ( "&" ) ; }
311
+
274
312
if ( writeTypes )
275
313
{
276
- WriteType ( context , declaration , emitContext . ThisType ) ;
314
+ WriteType ( context , declaration , declaration . ReturnType ) ;
277
315
Writer . Write ( ' ' ) ;
278
316
}
279
- else if ( thisCastType is not null )
280
- {
281
- Writer . Write ( '(' ) ;
282
- WriteType ( context , declaration , thisCastType ) ;
283
- Writer . Write ( ')' ) ;
284
- }
285
317
286
- Writer . WriteIdentifier ( emitContext . ThisParameterName ) ;
318
+ Writer . WriteIdentifier ( emitContext . ReturnBufferParameterName ) ;
287
319
first = false ;
288
320
}
289
321
290
- // Write out the return buffer parameter
291
- if ( declaration . ReturnByReference )
322
+ // Write out before-this return buffer
323
+ if ( declaration . ReturnByReference && ! declaration . FunctionAbi . ReturnInfo . Flags . HasFlag ( PathogenArgumentFlags . IsSRetAfterThis ) )
324
+ { WriteOutReturnBuffer ( emitContext ) ; }
325
+
326
+ // Write out the this pointer
327
+ if ( emitContext . ThisType is not null )
292
328
{
293
329
if ( ! first )
294
330
{ Writer . Write ( ", " ) ; }
295
331
296
- if ( ! declaration . IsVirtual )
297
- { Writer . Write ( "out " ) ; }
298
- else if ( mode == EmitParameterListMode . TrampolineArguments )
299
- { Writer . Write ( "&" ) ; }
300
-
301
332
if ( writeTypes )
302
333
{
303
- WriteType ( context , declaration , declaration . ReturnType ) ;
334
+ WriteType ( context , declaration , emitContext . ThisType ) ;
304
335
Writer . Write ( ' ' ) ;
305
336
}
337
+ else if ( thisCastType is not null )
338
+ {
339
+ Writer . Write ( '(' ) ;
340
+ WriteType ( context , declaration , thisCastType ) ;
341
+ Writer . Write ( ')' ) ;
342
+ }
306
343
307
- Writer . WriteIdentifier ( emitContext . ReturnBufferParameterName ) ;
344
+ Writer . WriteIdentifier ( emitContext . ThisParameterName ) ;
308
345
first = false ;
309
346
}
347
+
348
+ // Write out after-this return buffer
349
+ if ( declaration . ReturnByReference && declaration . FunctionAbi . ReturnInfo . Flags . HasFlag ( PathogenArgumentFlags . IsSRetAfterThis ) )
350
+ { WriteOutReturnBuffer ( emitContext ) ; }
310
351
}
311
352
312
353
// Write out parameters
0 commit comments