Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 37 additions & 10 deletions CelesteAnalyzer/CelesteAnalyzer/HookAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System.Collections;
using System.Collections.Immutable;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
Expand Down Expand Up @@ -41,16 +43,16 @@ private void AnalyzeEventAssignment(OperationAnalysisContext context)
{
// look for On/IL hook assignment to MonoMod.RuntimeDetour.HookGen

if (context.Operation.SemanticModel is not { } sem)
return;
if (context.Operation is not IEventAssignmentOperation op)
return;
if (op.Syntax is not AssignmentExpressionSyntax assignment)
return;
if (context.Operation.SemanticModel is not { } sem)
return;
if (sem.GetOperation(assignment.Left) is not IEventReferenceOperation refOp)
return;
if (Utils.BottommostNamespace(refOp.Event)?.Name is "On" or "IL")
AnalyzeHookFromLambdaOrIdentifier(context, assignment.Right, sem);
AnalyzeHookFromLambdaOrIdentifier(context, assignment.Right, sem, context.CancellationToken);
}

private void AnalyzeObjectCreationOperation(OperationAnalysisContext context)
Expand All @@ -74,17 +76,19 @@ private void AnalyzeObjectCreationOperation(OperationAnalysisContext context)

var targetArg = creationSyntax.ArgumentList!.Arguments[1];

AnalyzeHookFromLambdaOrIdentifier(context, targetArg.Expression, sem);
AnalyzeHookFromLambdaOrIdentifier(context, targetArg.Expression, sem, context.CancellationToken);
}

private static void AnalyzeHookFromLambdaOrIdentifier(OperationAnalysisContext context, ExpressionSyntax targetArg, SemanticModel sem)
private static void AnalyzeHookFromLambdaOrIdentifier(
OperationAnalysisContext context, ExpressionSyntax targetArg, SemanticModel sem,
CancellationToken ct)
{
// check method references
if (targetArg is IdentifierNameSyntax id)
{
if (Utils.GetMethodDeclarationSyntaxFromIdentifier(id, sem, out var methodRef) is { } syntax)
{
AnalyzeHook(context, methodRef!.Method, syntax.Body, syntax.GetLocation());
AnalyzeHook(context, methodRef!.Method, syntax.Body, syntax.GetLocation(), ct);
}
}

Expand All @@ -99,12 +103,13 @@ private static void AnalyzeHookFromLambdaOrIdentifier(OperationAnalysisContext c
.OfType<LambdaExpressionSyntax>()
.FirstOrDefault() is { } syntax)
{
AnalyzeHook(context, methodRef.Symbol, (SyntaxNode?)syntax.Block ?? syntax.ExpressionBody, syntax.GetLocation());
AnalyzeHook(context, methodRef.Symbol, (SyntaxNode?)syntax.Block ?? syntax.ExpressionBody, syntax.GetLocation(), ct);
}
}
}

private static void AnalyzeHook(OperationAnalysisContext context, IMethodSymbol methodSymbol, SyntaxNode? bodySyntax, Location loc)
private static void AnalyzeHook(OperationAnalysisContext context, IMethodSymbol methodSymbol, SyntaxNode? bodySyntax,
Location loc, CancellationToken ct)
{
var firstParam = methodSymbol.Parameters.First();

Expand All @@ -127,6 +132,8 @@ private static void AnalyzeHook(OperationAnalysisContext context, IMethodSymbol

foreach (var st in bodySyntax.DescendantNodes())
{
if (ct.IsCancellationRequested)
break;
if (IsOrig(st, firstParam))
{
origCalled = true;
Expand All @@ -150,8 +157,28 @@ private static void AnalyzeHook(OperationAnalysisContext context, IMethodSymbol

static bool IsOrig(SyntaxNode? st, IParameterSymbol orig)
{
return st is InvocationExpressionSyntax invocationExpressionSyntax &&
invocationExpressionSyntax.Expression.ToString() == orig.Name;
// orig?.Invoke(...)
if (st is ConditionalAccessExpressionSyntax cond
&& cond.WhenNotNull is InvocationExpressionSyntax notNullInvocationExpr
&& notNullInvocationExpr.Expression.ToString() == ".Invoke"
&& cond.Expression.ToString() == orig.Name)
{
return true;
}
Comment on lines +165 to +167

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: why is the block necessary? why not:

Suggested change
{
return true;
}
return true;


if (st is not InvocationExpressionSyntax invocationExpressionSyntax)
return false;

// orig.Invoke(...)
if (invocationExpressionSyntax.Expression is MemberAccessExpressionSyntax memberAccessExpressionSyntax
&& memberAccessExpressionSyntax.Name.ToString() == "Invoke"
&& memberAccessExpressionSyntax.Expression.ToString() == orig.Name)
{
return true;
}
Comment on lines +176 to +178

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick:

Suggested change
{
return true;
}
return true;


// orig(...);
return invocationExpressionSyntax.Expression.ToString() == orig.Name;
}
}

Expand Down