Roslyn: Analiza przepływu danych–semantyczny model

Semantyczny model dostarcza nam wiele informacji o kodzie, które zwykle uzyskuje się po kompilacji. Na przykład przeładowanie metod jest dużo łatwiejsze do określenia już po kompilacji.  Oznacza to, że nie jest już to  klasyczna, statyczna analiza kodu. Z tego względu, najpierw danych kod należy skompilować za pomocą:

var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location); var compilation = CSharpCompilation.Create("TestAssembly", new[] { tree }, new[] { mscorlib }); SemanticModel model = compilation.GetSemanticModel(tree);

Posiadając semantyczny model, możemy dokonać tzw. analizy przepływu danych. Jak sama nazwa wskazuje, dzięki niej będziemy wiedzieli, co dzieje się z naszymi danymi, a w szczególności:

  1. Jakie zmienne są odczytywane
  2. Jakie zmienne są zapisywane
  3. Jakie zmienne są deklarowane
  4. Co dzieje się ze zmiennymi w wewnątrz i na zewnątrz bloku

Jak widać, w celu uzyskania modelu, wystarczy wywołać GetSemanticModel na kompilacji.  Ponadto załóżmy, ze powyższy SyntacTree zawiera następujący kod:

SyntaxTree tree = CSharpSyntaxTree.ParseText(@" public class Class1 { public void TestMethod1(string a) { int localA=3,localB=5; if(a!=null) { localA++; } localB++; } } ");

Następnie, w celu dokonania analizy instrukcji warunkowej “if(a!=null)” wystarczy:

var node = tree.GetRoot() .DescendantNodes() .OfType<BlockSyntax>() .First() .DescendantNodes() .OfType<IfStatementSyntax>() .First(); DataFlowAnalysis dataFlowResult = model.AnalyzeDataFlow(node);

Obiekt DataFlowAnalysis zawiera następujące pola:

public abstract class DataFlowAnalysis { public abstract ImmutableArray<ISymbol> VariablesDeclared { get; } public abstract ImmutableArray<ISymbol> DataFlowsIn { get; } public abstract ImmutableArray<ISymbol> DataFlowsOut { get; } public abstract ImmutableArray<ISymbol> AlwaysAssigned { get; } public abstract ImmutableArray<ISymbol> ReadInside { get; } public abstract ImmutableArray<ISymbol> WrittenInside { get; } public abstract ImmutableArray<ISymbol> ReadOutside { get; } public abstract ImmutableArray<ISymbol> WrittenOutside { get; } public abstract ImmutableArray<ISymbol> Captured { get; } public abstract ImmutableArray<ISymbol> UnsafeAddressTaken { get; } public abstract bool Succeeded { get; } }

Jak widzimy, struktura zawiera mnóstwo pól opisujących co dzieje się z danymi we wskazanym bloku, w naszym przypadku w instrukcji warunkowej.

Na przykład WrittenInside zawiera zmienne, które są zapisywane w danym bloku. Dla powyższego przykładu jest to “localA”. VariablesDeclared będzie puste ponieważ nie deklarujemy żadnych zmiennych w powyższym IF. DataFlowsIn zawiera z kolei “localA” oraz “a” ponieważ to one uczestniczą w DataFlow.

SemanticModel jest przydatny jeszcze w kilku innych analizach, o których będę pisał w następnych postach.

Leave a Reply

Your email address will not be published.