Skip to content

Commit

Permalink
Merge pull request #135 from Washi1337/feature/new-ast-builder
Browse files Browse the repository at this point in the history
New AST builder.
  • Loading branch information
Washi1337 committed Feb 11, 2024
2 parents bb175d0 + e00a467 commit 64e2779
Show file tree
Hide file tree
Showing 109 changed files with 4,434 additions and 2,073 deletions.
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,21 @@ Main Features
- Data flow analysis
- Create data flow graphs
- Inspect stack and variable dependencies of instructions.
- AST building
- Lift control flow graphs to Abstract Syntax Trees (ASTs).
- Unified generic API.
- Serialize any kind of graph to the dot file format.
- Adding a new platform for flow analysis requires minimal effort


Supported platforms:

| Architecture | Back-end | Control Flow | Data Flow | Purity Classification | Emulation |
|--------------|---------------------------------------------------------|--------------|-----------|-----------------------|-----------|
| CIL | [AsmResolver](https://github.com/Washi1337/AsmResolver) |||| ✓ (WIP) |
| CIL | [dnlib](https://github.com/0xd4d/dnlib) |||| |
| x86 (32-bit) | [Iced](https://github.com/icedland/iced) ||| | |
| x86 (64-bit) | [Iced](https://github.com/icedland/iced) ||| | |
| Architecture | Back-end | Control Flow | Data Flow | AST | Purity Classification | Emulation |
|--------------|---------------------------------------------------------|--------------|-----------|-----|-----------------------|-----------|
| CIL | [AsmResolver](https://github.com/Washi1337/AsmResolver) |||| | ✓ (WIP) |
| CIL | [dnlib](https://github.com/0xd4d/dnlib) |||| | |
| x86 (32-bit) | [Iced](https://github.com/icedland/iced) ||| | | |
| x86 (64-bit) | [Iced](https://github.com/icedland/iced) ||| | | |


Compiling
Expand Down Expand Up @@ -54,5 +56,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).

Found a bug or have questions?
------------------------------
Please use the [issue tracker](https://github.com/Washi1337/Echo/issues). Try to be as descriptive as possible.

Please use the [issue tracker](https://github.com/Washi1337/Echo/issues). Try to be as descriptive as possible.
34 changes: 34 additions & 0 deletions src/Core/Echo.Ast/Analysis/AstPurityClassifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Echo.Code;

namespace Echo.Ast.Analysis;

/// <summary>
/// Provides a wrapper around a <see cref="IPurityClassifier{TInstruction}"/> that is able to classify statements
/// and expressions with <typeparamref name="TInstruction"/> instructions by purity.
/// </summary>
/// <typeparam name="TInstruction">The type of instructions the statements store.</typeparam>
public class AstPurityClassifier<TInstruction> : IPurityClassifier<Statement<TInstruction>>
{
/// <summary>
/// Creates a new instance of the <see cref="AstPurityClassifier{TInstruction}"/> class.
/// </summary>
/// <param name="baseClassifier">The base classifier to use for classifying individual instructions in the AST.</param>
public AstPurityClassifier(IPurityClassifier<TInstruction> baseClassifier)
{
BaseClassifier = baseClassifier;
}

/// <summary>
/// Gets the base classifier to use for classifying individual instructions in the AST.
/// </summary>
public IPurityClassifier<TInstruction> BaseClassifier
{
get;
}

/// <inheritdoc />
public Trilean IsPure(in Statement<TInstruction> instruction)
{
return instruction.Accept(AstPurityVisitor<TInstruction>.Instance, BaseClassifier);
}
}
89 changes: 89 additions & 0 deletions src/Core/Echo.Ast/Analysis/AstPurityVisitor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using Echo.Code;

namespace Echo.Ast.Analysis;

/// <summary>
/// Provides a mechanism for traversing an AST and determining its purity.
/// </summary>
/// <typeparam name="TInstruction">The type of instructions to store in each expression.</typeparam>
public class AstPurityVisitor<TInstruction> : IAstNodeVisitor<TInstruction, IPurityClassifier<TInstruction>, Trilean>
{
/// <summary>
/// Gets the singleton instance of the <see cref="AstPurityVisitor{TInstruction}"/> class.
/// </summary>
public static AstPurityVisitor<TInstruction> Instance
{
get;
} = new();

/// <inheritdoc />
public Trilean Visit(CompilationUnit<TInstruction> unit, IPurityClassifier<TInstruction> state)
{
return unit.Root.Accept(this, state);
}

/// <inheritdoc />
public Trilean Visit(AssignmentStatement<TInstruction> statement, IPurityClassifier<TInstruction> state) => false;

/// <inheritdoc />
public Trilean Visit(ExpressionStatement<TInstruction> statement, IPurityClassifier<TInstruction> state)
{
return statement.Expression.Accept(this, state);
}

/// <inheritdoc />
public Trilean Visit(PhiStatement<TInstruction> statement, IPurityClassifier<TInstruction> state) => false;

/// <inheritdoc />
public Trilean Visit(BlockStatement<TInstruction> statement, IPurityClassifier<TInstruction> state)
{
var result = Trilean.True;

for (int i = 0; i < statement.Statements.Count && result != Trilean.False; i++)
result &= statement.Statements[i].Accept(this, state);

return result;
}

/// <inheritdoc />
public Trilean Visit(ExceptionHandlerStatement<TInstruction> statement, IPurityClassifier<TInstruction> state)
{
var result = statement.ProtectedBlock.Accept(this, state);

for (int i = 0; i < statement.Handlers.Count && result != Trilean.False; i++)
result &= statement.Handlers[i].Accept(this, state);

return result;
}

/// <inheritdoc />
public Trilean Visit(HandlerClause<TInstruction> clause, IPurityClassifier<TInstruction> state)
{
var result = Trilean.True;

if (clause.Prologue is not null)
result &= clause.Prologue.Accept(this, state);

if (result.ToBooleanOrFalse())
result &= clause.Contents.Accept(this, state);

if (clause.Epilogue is not null && result.ToBooleanOrFalse())
result &= clause.Epilogue.Accept(this, state);

return result;
}

/// <inheritdoc />
public Trilean Visit(InstructionExpression<TInstruction> expression, IPurityClassifier<TInstruction> state)
{
var result = state.IsPure(expression.Instruction);

for (int i = 0; i < expression.Arguments.Count && result != Trilean.False; i++)
result &= expression.Arguments[i].Accept(this, state);

return result;
}

/// <inheritdoc />
public Trilean Visit(VariableExpression<TInstruction> expression, IPurityClassifier<TInstruction> state) => true;
}
41 changes: 31 additions & 10 deletions src/Core/Echo.Ast/Analysis/FlowControlDeterminer.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,49 @@
using Echo.Code;
using System;
using Echo.Code;

namespace Echo.Ast.Analysis
{
internal sealed class FlowControlDeterminer<TInstruction>
: IAstNodeVisitor<TInstruction, object, InstructionFlowControl>
: IAstNodeVisitor<TInstruction, object?, InstructionFlowControl>
{
private readonly IArchitecture<TInstruction> _isa;

internal FlowControlDeterminer(IArchitecture<TInstruction> isa) =>
_isa = isa;

public InstructionFlowControl Visit(AssignmentStatement<TInstruction> assignmentStatement, object state) =>
assignmentStatement.Expression.Accept(this, state);
public InstructionFlowControl Visit(CompilationUnit<TInstruction> unit, object? state) =>
throw new NotSupportedException();

public InstructionFlowControl Visit(ExpressionStatement<TInstruction> expressionStatement, object state) =>
expressionStatement.Expression.Accept(this, state);
public InstructionFlowControl Visit(AssignmentStatement<TInstruction> statement, object? state) =>
statement.Expression.Accept(this, state);

public InstructionFlowControl Visit(PhiStatement<TInstruction> phiStatement, object state) =>
public InstructionFlowControl Visit(ExpressionStatement<TInstruction> statement, object? state) =>
statement.Expression.Accept(this, state);

public InstructionFlowControl Visit(PhiStatement<TInstruction> statement, object? state) =>
InstructionFlowControl.Fallthrough;

public InstructionFlowControl Visit(InstructionExpression<TInstruction> instructionExpression, object state) =>
_isa.GetFlowControl(instructionExpression.Instruction);
public InstructionFlowControl Visit(BlockStatement<TInstruction> statement, object? state)
{
return statement.Statements.Count > 0
? statement.Statements[statement.Statements.Count - 1].Accept(this, state)
: InstructionFlowControl.Fallthrough;
}

public InstructionFlowControl Visit(ExceptionHandlerStatement<TInstruction> statement, object? state)
{
return statement.ProtectedBlock.Accept(this, state);
}

public InstructionFlowControl Visit(HandlerClause<TInstruction> clause, object? state)
{
throw new NotSupportedException();
}

public InstructionFlowControl Visit(InstructionExpression<TInstruction> expression, object? state) =>
_isa.GetFlowControl(expression.Instruction);

public InstructionFlowControl Visit(VariableExpression<TInstruction> variableExpression, object state) =>
public InstructionFlowControl Visit(VariableExpression<TInstruction> expression, object? state) =>
InstructionFlowControl.Fallthrough;
}
}
41 changes: 41 additions & 0 deletions src/Core/Echo.Ast/Analysis/ReadVariableFinder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Buffers;
using System.Collections.Generic;
using Echo.Code;

namespace Echo.Ast.Analysis
{
internal sealed class ReadVariableFinder<TInstruction> : AstNodeListener<TInstruction>
{
private readonly IArchitecture<TInstruction> _architecture;

public ReadVariableFinder(IArchitecture<TInstruction> architecture)
{
_architecture = architecture;
}

internal HashSet<IVariable> Variables { get; } = new();

public override void ExitVariableExpression(VariableExpression<TInstruction> expression)
{
base.ExitVariableExpression(expression);
Variables.Add(expression.Variable);
}

public override void ExitInstructionExpression(InstructionExpression<TInstruction> expression)
{
int count = _architecture.GetReadVariablesCount(expression.Instruction);
if (count == 0)
return;

var variables = ArrayPool<IVariable>.Shared.Rent(count);

int actualCount = _architecture.GetReadVariables(expression.Instruction, variables);
for (int i = 0; i < actualCount; i++)
Variables.Add(variables[i]);

ArrayPool<IVariable>.Shared.Return(variables);

base.ExitInstructionExpression(expression);
}
}
}
18 changes: 0 additions & 18 deletions src/Core/Echo.Ast/Analysis/ReadVariableFinderWalker.cs

This file was deleted.

48 changes: 48 additions & 0 deletions src/Core/Echo.Ast/Analysis/WrittenVariableFinder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System.Buffers;
using System.Collections.Generic;
using Echo.Code;

namespace Echo.Ast.Analysis
{
internal sealed class WrittenVariableFinder<TInstruction> : AstNodeListener<TInstruction>
{
private readonly IArchitecture<TInstruction> _architecture;

public WrittenVariableFinder(IArchitecture<TInstruction> architecture)
{
_architecture = architecture;
}

internal HashSet<IVariable> Variables { get; } = new();

public override void ExitAssignmentStatement(AssignmentStatement<TInstruction> statement)
{
base.ExitAssignmentStatement(statement);
for (int i = 0; i < statement.Variables.Count; i++)
Variables.Add(statement.Variables[i]);
}

public override void ExitPhiStatement(PhiStatement<TInstruction> phiStatement)
{
base.ExitPhiStatement(phiStatement);
Variables.Add(phiStatement.Representative);
}

public override void ExitInstructionExpression(InstructionExpression<TInstruction> expression)
{
int count = _architecture.GetWrittenVariablesCount(expression.Instruction);
if (count == 0)
return;

var variables = ArrayPool<IVariable>.Shared.Rent(count);

int actualCount = _architecture.GetWrittenVariables(expression.Instruction, variables);
for (int i = 0; i < actualCount; i++)
Variables.Add(variables[i]);

ArrayPool<IVariable>.Shared.Return(variables);

base.ExitInstructionExpression(expression);
}
}
}
24 changes: 0 additions & 24 deletions src/Core/Echo.Ast/Analysis/WrittenVariableFinderWalker.cs

This file was deleted.

Loading

0 comments on commit 64e2779

Please sign in to comment.