Skip to content

Commit 5fa0dd7

Browse files
authored
Merge pull request #4619 from tamasvajk/feature/csharp9-function-pointer
C#: Extract function pointers
2 parents 2f459d9 + 70c302f commit 5fa0dd7

27 files changed

+8053
-3442
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
lgtm,codescanning
2+
* Function pointer types (`FunctionPointerType`) and call to function pointers
3+
(`FunctionPointerCall`) are extracted.

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,11 @@ internal static class CallTypeExtensions
316316
/// </summary>
317317
public static ExprKind AdjustKind(this Expression.CallType ct, ExprKind k)
318318
{
319+
if (k == ExprKind.ADDRESS_OF)
320+
{
321+
return k;
322+
}
323+
319324
switch (ct)
320325
{
321326
case Expression.CallType.Dynamic:

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Microsoft.CodeAnalysis.CSharp;
55
using Semmle.Extraction.Kinds;
66
using System.IO;
7+
using System;
78

89
namespace Semmle.Extraction.CSharp.Entities.Expressions
910
{
@@ -66,7 +67,7 @@ protected override void PopulateExpression(TextWriter trapFile)
6667
}
6768
break;
6869
default:
69-
// Delegate call; `d()`
70+
// Delegate or function pointer call; `d()`
7071
Create(cx, Syntax.Expression, this, child++);
7172
break;
7273
}
@@ -84,7 +85,7 @@ protected override void PopulateExpression(TextWriter trapFile)
8485

8586
if (target == null)
8687
{
87-
if (!isDynamicCall && !IsDelegateCall(info))
88+
if (!isDynamicCall && !IsDelegateLikeCall(info))
8889
cx.ModelError(Syntax, "Unable to resolve target for call. (Compilation error?)");
8990
return;
9091
}
@@ -98,7 +99,7 @@ private static bool IsDynamicCall(ExpressionNodeInfo info)
9899
// Either the qualifier (Expression) is dynamic,
99100
// or one of the arguments is dynamic.
100101
var node = (InvocationExpressionSyntax)info.Node;
101-
return !IsDelegateCall(info) &&
102+
return !IsDelegateLikeCall(info) &&
102103
(IsDynamic(info.Context, node.Expression) || node.ArgumentList.Arguments.Any(arg => IsDynamic(info.Context, arg.Expression)));
103104
}
104105

@@ -133,12 +134,22 @@ public IMethodSymbol TargetSymbol
133134
}
134135
}
135136

136-
private static bool IsDelegateCall(ExpressionNodeInfo info)
137+
private static bool IsDelegateLikeCall(ExpressionNodeInfo info)
138+
{
139+
return IsDelegateLikeCall(info, symbol => IsFunctionPointer(symbol) || IsDelegateInvoke(symbol));
140+
}
141+
142+
private static bool IsDelegateInvokeCall(ExpressionNodeInfo info)
143+
{
144+
return IsDelegateLikeCall(info, IsDelegateInvoke);
145+
}
146+
147+
private static bool IsDelegateLikeCall(ExpressionNodeInfo info, Func<ISymbol, bool> check)
137148
{
138149
var si = info.SymbolInfo;
139150

140151
if (si.CandidateReason == CandidateReason.OverloadResolutionFailure &&
141-
si.CandidateSymbols.OfType<IMethodSymbol>().All(s => s.MethodKind == MethodKind.DelegateInvoke))
152+
si.CandidateSymbols.All(check))
142153
{
143154
return true;
144155
}
@@ -153,9 +164,20 @@ node.Expression is IdentifierNameSyntax &&
153164
return true;
154165
}
155166

156-
return si.Symbol != null &&
157-
si.Symbol.Kind == SymbolKind.Method &&
158-
((IMethodSymbol)si.Symbol).MethodKind == MethodKind.DelegateInvoke;
167+
return check(si.Symbol);
168+
}
169+
170+
private static bool IsFunctionPointer(ISymbol symbol)
171+
{
172+
return symbol != null &&
173+
symbol.Kind == SymbolKind.FunctionPointerType;
174+
}
175+
176+
private static bool IsDelegateInvoke(ISymbol symbol)
177+
{
178+
return symbol != null &&
179+
symbol.Kind == SymbolKind.Method &&
180+
((IMethodSymbol)symbol).MethodKind == MethodKind.DelegateInvoke;
159181
}
160182

161183
private static bool IsLocalFunctionInvocation(ExpressionNodeInfo info)
@@ -168,8 +190,10 @@ private static ExprKind GetKind(ExpressionNodeInfo info)
168190
{
169191
return IsNameof((InvocationExpressionSyntax)info.Node)
170192
? ExprKind.NAMEOF
171-
: IsDelegateCall(info)
172-
? ExprKind.DELEGATE_INVOCATION
193+
: IsDelegateLikeCall(info)
194+
? IsDelegateInvokeCall(info)
195+
? ExprKind.DELEGATE_INVOCATION
196+
: ExprKind.FUNCTION_POINTER_INVOCATION
173197
: IsLocalFunctionInvocation(info)
174198
? ExprKind.LOCAL_FUNCTION_INVOCATION
175199
: ExprKind.METHOD_INVOCATION;

csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public override void Populate(TextWriter trapFile)
3939
var originalDefinition = IsSourceDeclaration ? this : Create(Context, symbol.OriginalDefinition);
4040
var returnType = Type.Create(Context, symbol.ReturnType);
4141
trapFile.local_functions(this, symbol.Name, returnType, originalDefinition);
42-
ExtractRefReturn(trapFile);
42+
ExtractRefReturn(trapFile, symbol, this);
4343
}
4444

4545
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NeedsLabel;

csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ protected void PopulateParameters()
4040

4141
foreach (var p in parameters.Zip(originalParameters, (paramSymbol, originalParam) => new { paramSymbol, originalParam }))
4242
{
43-
var original = SymbolEqualityComparer.Default.Equals(p.paramSymbol, p.originalParam) ? null : Parameter.Create(Context, p.originalParam, originalMethod);
43+
var original = SymbolEqualityComparer.Default.Equals(p.paramSymbol, p.originalParam)
44+
? null
45+
: Parameter.Create(Context, p.originalParam, originalMethod);
4446
Parameter.Create(Context, p.paramSymbol, this, original);
4547
}
4648

@@ -110,7 +112,7 @@ public void Overrides(TextWriter trapFile)
110112
/// <summary>
111113
/// Factored out to share logic between `Method` and `UserOperator`.
112114
/// </summary>
113-
protected static void BuildMethodId(Method m, TextWriter trapFile)
115+
private static void BuildMethodId(Method m, TextWriter trapFile)
114116
{
115117
m.symbol.ReturnType.BuildOrWriteId(m.Context, trapFile, m.symbol);
116118
trapFile.Write(" ");
@@ -324,12 +326,12 @@ protected void PopulateGenerics(TextWriter trapFile)
324326
}
325327
}
326328

327-
protected void ExtractRefReturn(TextWriter trapFile)
329+
public static void ExtractRefReturn(TextWriter trapFile, IMethodSymbol method, IEntity element)
328330
{
329-
if (symbol.ReturnsByRef)
330-
trapFile.type_annotation(this, Kinds.TypeAnnotation.Ref);
331-
if (symbol.ReturnsByRefReadonly)
332-
trapFile.type_annotation(this, Kinds.TypeAnnotation.ReadonlyRef);
331+
if (method.ReturnsByRef)
332+
trapFile.type_annotation(element, Kinds.TypeAnnotation.Ref);
333+
if (method.ReturnsByRefReadonly)
334+
trapFile.type_annotation(element, Kinds.TypeAnnotation.ReadonlyRef);
333335
}
334336

335337
protected void PopulateMethod(TextWriter trapFile)

csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public override void Populate(TextWriter trapFile)
4949

5050
PopulateGenerics(trapFile);
5151
Overrides(trapFile);
52-
ExtractRefReturn(trapFile);
52+
ExtractRefReturn(trapFile, symbol, this);
5353
ExtractCompilerGenerated(trapFile);
5454
}
5555

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System.IO;
2+
using System.Linq;
3+
using Microsoft.CodeAnalysis;
4+
5+
namespace Semmle.Extraction.CSharp.Entities
6+
{
7+
internal class FunctionPointerType : Type<IFunctionPointerTypeSymbol>
8+
{
9+
private FunctionPointerType(Context cx, IFunctionPointerTypeSymbol init)
10+
: base(cx, init)
11+
{
12+
}
13+
14+
public override void WriteId(TextWriter trapFile)
15+
{
16+
symbol.BuildTypeId(Context, trapFile, symbol);
17+
trapFile.Write(";functionpointertype");
18+
}
19+
20+
public override bool NeedsPopulation => true;
21+
22+
public override void Populate(TextWriter trapFile)
23+
{
24+
trapFile.function_pointer_calling_conventions(this, (int)symbol.Signature.CallingConvention);
25+
foreach (var (conv, i) in symbol.Signature.UnmanagedCallingConventionTypes.Select((nt, i) => (Create(Context, nt), i)))
26+
{
27+
trapFile.has_unmanaged_calling_conventions(this, i, conv.TypeRef);
28+
}
29+
30+
PopulateType(trapFile);
31+
}
32+
33+
public static FunctionPointerType Create(Context cx, IFunctionPointerTypeSymbol symbol) => FunctionPointerTypeFactory.Instance.CreateEntityFromSymbol(cx, symbol);
34+
35+
private class FunctionPointerTypeFactory : ICachedEntityFactory<IFunctionPointerTypeSymbol, FunctionPointerType>
36+
{
37+
public static FunctionPointerTypeFactory Instance { get; } = new FunctionPointerTypeFactory();
38+
39+
public FunctionPointerType Create(Context cx, IFunctionPointerTypeSymbol init) => new FunctionPointerType(cx, init);
40+
}
41+
}
42+
}

csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Microsoft.CodeAnalysis;
22
using Microsoft.CodeAnalysis.CSharp.Syntax;
33
using Semmle.Util;
4+
using System;
45
using System.Collections.Generic;
56
using System.IO;
67
using System.Linq;
@@ -57,6 +58,7 @@ private static Kinds.TypeKind GetClassType(Context cx, ITypeSymbol t, bool const
5758
case TypeKind.Enum: return Kinds.TypeKind.ENUM;
5859
case TypeKind.Delegate: return Kinds.TypeKind.DELEGATE;
5960
case TypeKind.Pointer: return Kinds.TypeKind.POINTER;
61+
case TypeKind.FunctionPointer: return Kinds.TypeKind.FUNCTION_POINTER;
6062
case TypeKind.Error: return Kinds.TypeKind.UNKNOWN;
6163
default:
6264
cx.ModelError(t, $"Unhandled type kind '{t.TypeKind}'");
@@ -131,23 +133,14 @@ protected void PopulateType(TextWriter trapFile, bool constructUnderlyingTupleTy
131133
// This is a delegate.
132134
// The method "Invoke" has the return type.
133135
var invokeMethod = ((INamedTypeSymbol)symbol).DelegateInvokeMethod;
136+
ExtractParametersForDelegateLikeType(trapFile, invokeMethod,
137+
t => trapFile.delegate_return_type(this, t));
138+
}
134139

135-
// Copy the parameters from the "Invoke" method to the delegate type
136-
for (var i = 0; i < invokeMethod.Parameters.Length; ++i)
137-
{
138-
var param = invokeMethod.Parameters[i];
139-
var originalParam = invokeMethod.OriginalDefinition.Parameters[i];
140-
var originalParamEntity = SymbolEqualityComparer.Default.Equals(param, originalParam) ? null :
141-
DelegateTypeParameter.Create(Context, originalParam, Create(Context, ((INamedTypeSymbol)symbol).OriginalDefinition));
142-
DelegateTypeParameter.Create(Context, param, this, originalParamEntity);
143-
}
144-
145-
var returnKey = Create(Context, invokeMethod.ReturnType);
146-
trapFile.delegate_return_type(this, returnKey.TypeRef);
147-
if (invokeMethod.ReturnsByRef)
148-
trapFile.type_annotation(this, Kinds.TypeAnnotation.Ref);
149-
if (invokeMethod.ReturnsByRefReadonly)
150-
trapFile.type_annotation(this, Kinds.TypeAnnotation.ReadonlyRef);
140+
if (symbol is IFunctionPointerTypeSymbol functionPointer)
141+
{
142+
ExtractParametersForDelegateLikeType(trapFile, functionPointer.Signature,
143+
t => trapFile.function_pointer_return_type(this, t));
151144
}
152145

153146
Modifier.ExtractModifiers(Context, trapFile, this, symbol);
@@ -170,6 +163,23 @@ protected void PopulateType(TextWriter trapFile, bool constructUnderlyingTupleTy
170163
}
171164
}
172165

166+
private void ExtractParametersForDelegateLikeType(TextWriter trapFile, IMethodSymbol invokeMethod, Action<Type> storeReturnType)
167+
{
168+
for (var i = 0; i < invokeMethod.Parameters.Length; ++i)
169+
{
170+
var param = invokeMethod.Parameters[i];
171+
var originalParam = invokeMethod.OriginalDefinition.Parameters[i];
172+
var originalParamEntity = SymbolEqualityComparer.Default.Equals(param, originalParam)
173+
? null
174+
: DelegateTypeParameter.Create(Context, originalParam, Create(Context, ((INamedTypeSymbol)symbol).OriginalDefinition));
175+
DelegateTypeParameter.Create(Context, param, this, originalParamEntity);
176+
}
177+
178+
var returnKey = Create(Context, invokeMethod.ReturnType);
179+
storeReturnType(returnKey.TypeRef);
180+
Method.ExtractRefReturn(trapFile, invokeMethod, this);
181+
}
182+
173183
/// <summary>
174184
/// Called to extract all members and nested types.
175185
/// This is called on each member of a namespace,
@@ -265,8 +275,7 @@ public static bool IsDelegate(ITypeSymbol symbol) =>
265275
symbol != null && symbol.TypeKind == TypeKind.Delegate;
266276

267277
/// <summary>
268-
/// A copy of a delegate "Invoke" method parameter used for the delgate
269-
/// type.
278+
/// A copy of a delegate "Invoke" method or function pointer parameter.
270279
/// </summary>
271280
private class DelegateTypeParameter : Parameter
272281
{

csharp/extractor/Semmle.Extraction.CSharp/Kinds/ExprKind.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ public enum ExprKind
120120
GT_PATTERN = 123,
121121
LE_PATTERN = 124,
122122
GE_PATTERN = 125,
123-
NOT_PATTERN = 126
123+
NOT_PATTERN = 126,
124+
FUNCTION_POINTER_INVOCATION = 129,
124125
}
125126
}

csharp/extractor/Semmle.Extraction.CSharp/Kinds/TypeKind.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public enum TypeKind
3434
DYNAMIC = 29,
3535
ARGLIST = 30,
3636
UNKNOWN = 31,
37-
TUPLE = 32
37+
TUPLE = 32,
38+
FUNCTION_POINTER = 33
3839
}
3940
}

0 commit comments

Comments
 (0)