Browse Source

Reactivate PatternStatementTransform

pull/728/head
Daniel Grunwald 9 years ago
parent
commit
bb1beedbd4
  1. 9
      ICSharpCode.Decompiler/CSharp/Annotations.cs
  2. 4
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  3. 78
      ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs
  4. 67
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
  5. 5
      ICSharpCode.Decompiler/CSharp/Transforms/TransformContext.cs
  6. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  7. 12
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  8. 3
      ICSharpCode.Decompiler/IL/Transforms/InlineCompilerGeneratedVariables.cs
  9. 3
      ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs
  10. 1
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

9
ICSharpCode.Decompiler/CSharp/Annotations.cs

@ -110,6 +110,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -110,6 +110,15 @@ namespace ICSharpCode.Decompiler.CSharp
{
return node.Annotation<ResolveResult>() ?? ErrorResolveResult.UnknownError;
}
public static ILVariable GetILVariable(this IdentifierExpression expr)
{
var rr = expr.Annotation<ResolveResult>() as ILVariableResolveResult;
if (rr != null)
return rr.Variable;
else
return null;
}
}
public class ILVariableResolveResult : ResolveResult

4
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -66,7 +66,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -66,7 +66,7 @@ namespace ICSharpCode.Decompiler.CSharp
List<IAstTransform> astTransforms = new List<IAstTransform> {
//new PushNegation(),
//new DelegateConstruction(context),
//new PatternStatementTransform(context),
new PatternStatementTransform(),
new ReplaceMethodCallsWithOperators(),
new IntroduceUnsafeModifier(),
new AddCheckedBlocks(),
@ -196,7 +196,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -196,7 +196,7 @@ namespace ICSharpCode.Decompiler.CSharp
void RunTransforms(AstNode rootNode, ITypeResolveContext decompilationContext)
{
var typeSystemAstBuilder = CreateAstBuilder(decompilationContext);
var context = new TransformContext(typeSystem, decompilationContext, typeSystemAstBuilder, CancellationToken);
var context = new TransformContext(typeSystem, decompilationContext, typeSystemAstBuilder, settings, CancellationToken);
foreach (var transform in astTransforms) {
CancellationToken.ThrowIfCancellationRequested();
transform.Run(rootNode, context);

78
ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
using System;
using System.Diagnostics;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.TypeSystem;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.CSharp.Transforms
@ -26,86 +27,81 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -26,86 +27,81 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// <summary>
/// Base class for AST visitors that need the current type/method context info.
/// </summary>
public abstract class ContextTrackingVisitor<TResult> : DepthFirstAstVisitor<object, TResult>, IAstTransform
public abstract class ContextTrackingVisitor<TResult> : DepthFirstAstVisitor<TResult>
{
protected readonly DecompilerContext context;
protected ITypeDefinition currentTypeDefinition;
protected IMethod currentMethod;
protected ContextTrackingVisitor(DecompilerContext context)
protected void Initialize(TransformContext context)
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
currentTypeDefinition = context.DecompiledTypeDefinition;
currentMethod = context.DecompiledMember as IMethod;
}
public override TResult VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
public override TResult VisitTypeDeclaration(TypeDeclaration typeDeclaration)
{
TypeDefinition oldType = context.CurrentType;
ITypeDefinition oldType = currentTypeDefinition;
try {
context.CurrentType = typeDeclaration.Annotation<TypeDefinition>();
return base.VisitTypeDeclaration(typeDeclaration, data);
currentTypeDefinition = typeDeclaration.GetSymbol() as ITypeDefinition;
return base.VisitTypeDeclaration(typeDeclaration);
} finally {
context.CurrentType = oldType;
currentTypeDefinition = oldType;
}
}
public override TResult VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data)
public override TResult VisitMethodDeclaration(MethodDeclaration methodDeclaration)
{
Debug.Assert(context.CurrentMethod == null);
Debug.Assert(currentMethod == null);
try {
context.CurrentMethod = methodDeclaration.Annotation<MethodDefinition>();
return base.VisitMethodDeclaration(methodDeclaration, data);
currentMethod = methodDeclaration.GetSymbol() as IMethod;
return base.VisitMethodDeclaration(methodDeclaration);
} finally {
context.CurrentMethod = null;
currentMethod = null;
}
}
public override TResult VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data)
public override TResult VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration)
{
Debug.Assert(context.CurrentMethod == null);
Debug.Assert(currentMethod == null);
try {
context.CurrentMethod = constructorDeclaration.Annotation<MethodDefinition>();
return base.VisitConstructorDeclaration(constructorDeclaration, data);
currentMethod = constructorDeclaration.GetSymbol() as IMethod;
return base.VisitConstructorDeclaration(constructorDeclaration);
} finally {
context.CurrentMethod = null;
currentMethod = null;
}
}
public override TResult VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, object data)
public override TResult VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration)
{
Debug.Assert(context.CurrentMethod == null);
Debug.Assert(currentMethod == null);
try {
context.CurrentMethod = destructorDeclaration.Annotation<MethodDefinition>();
return base.VisitDestructorDeclaration(destructorDeclaration, data);
currentMethod = destructorDeclaration.GetSymbol() as IMethod;
return base.VisitDestructorDeclaration(destructorDeclaration);
} finally {
context.CurrentMethod = null;
currentMethod = null;
}
}
public override TResult VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, object data)
public override TResult VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration)
{
Debug.Assert(context.CurrentMethod == null);
Debug.Assert(currentMethod == null);
try {
context.CurrentMethod = operatorDeclaration.Annotation<MethodDefinition>();
return base.VisitOperatorDeclaration(operatorDeclaration, data);
currentMethod = operatorDeclaration.GetSymbol() as IMethod;
return base.VisitOperatorDeclaration(operatorDeclaration);
} finally {
context.CurrentMethod = null;
currentMethod = null;
}
}
public override TResult VisitAccessor(Accessor accessor, object data)
public override TResult VisitAccessor(Accessor accessor)
{
Debug.Assert(context.CurrentMethod == null);
Debug.Assert(currentMethod == null);
try {
context.CurrentMethod = accessor.Annotation<MethodDefinition>();
return base.VisitAccessor(accessor, data);
currentMethod = accessor.GetSymbol() as IMethod;
return base.VisitAccessor(accessor);
} finally {
context.CurrentMethod = null;
currentMethod = null;
}
}
void IAstTransform.Run(AstNode node)
{
node.AcceptVisitor(this, null);
}
}
}

67
ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

@ -21,7 +21,6 @@ using System.Collections.Generic; @@ -21,7 +21,6 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.Analysis;
using ICSharpCode.NRefactory.PatternMatching;
@ -34,12 +33,17 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -34,12 +33,17 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// </summary>
public sealed class PatternStatementTransform : ContextTrackingVisitor<AstNode>, IAstTransform
{
public PatternStatementTransform(DecompilerContext context) : base(context)
TransformContext context;
public void Run(AstNode rootNode, TransformContext context)
{
this.context = context;
base.Initialize(context);
rootNode.AcceptVisitor(this);
}
#region Visitor Overrides
protected override AstNode VisitChildren(AstNode node, object data)
protected override AstNode VisitChildren(AstNode node)
{
// Go through the children, and keep visiting a node as long as it changes.
// Because some transforms delete/replace nodes before and after the node being transformed, we rely
@ -48,14 +52,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -48,14 +52,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
AstNode oldChild;
do {
oldChild = child;
child = child.AcceptVisitor(this, data);
child = child.AcceptVisitor(this);
Debug.Assert(child != null && child.Parent == node);
} while (child != oldChild);
}
return node;
}
public override AstNode VisitExpressionStatement(ExpressionStatement expressionStatement, object data)
public override AstNode VisitExpressionStatement(ExpressionStatement expressionStatement)
{
AstNode result;
if (context.Settings.UsingStatement)
@ -75,25 +79,25 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -75,25 +79,25 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (result != null)
return result;
}
return base.VisitExpressionStatement(expressionStatement, data);
return base.VisitExpressionStatement(expressionStatement);
}
public override AstNode VisitUsingStatement(UsingStatement usingStatement, object data)
public override AstNode VisitUsingStatement(UsingStatement usingStatement)
{
if (context.Settings.ForEachStatement) {
AstNode result = TransformForeach(usingStatement);
if (result != null)
return result;
}
return base.VisitUsingStatement(usingStatement, data);
return base.VisitUsingStatement(usingStatement);
}
public override AstNode VisitWhileStatement(WhileStatement whileStatement, object data)
public override AstNode VisitWhileStatement(WhileStatement whileStatement)
{
return TransformDoWhile(whileStatement) ?? base.VisitWhileStatement(whileStatement, data);
return TransformDoWhile(whileStatement) ?? base.VisitWhileStatement(whileStatement);
}
public override AstNode VisitIfElseStatement(IfElseStatement ifElseStatement, object data)
public override AstNode VisitIfElseStatement(IfElseStatement ifElseStatement)
{
if (context.Settings.SwitchStatementOnString) {
AstNode result = TransformSwitchOnString(ifElseStatement);
@ -103,23 +107,23 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -103,23 +107,23 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
AstNode simplifiedIfElse = SimplifyCascadingIfElseStatements(ifElseStatement);
if (simplifiedIfElse != null)
return simplifiedIfElse;
return base.VisitIfElseStatement(ifElseStatement, data);
return base.VisitIfElseStatement(ifElseStatement);
}
public override AstNode VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data)
public override AstNode VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration)
{
if (context.Settings.AutomaticProperties) {
AstNode result = TransformAutomaticProperties(propertyDeclaration);
if (result != null)
return result;
}
return base.VisitPropertyDeclaration(propertyDeclaration, data);
return base.VisitPropertyDeclaration(propertyDeclaration);
}
public override AstNode VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration, object data)
public override AstNode VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration)
{
// first apply transforms to the accessor bodies
base.VisitCustomEventDeclaration(eventDeclaration, data);
base.VisitCustomEventDeclaration(eventDeclaration);
if (context.Settings.AutomaticEvents) {
AstNode result = TransformAutomaticEvents(eventDeclaration);
if (result != null)
@ -128,14 +132,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -128,14 +132,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return eventDeclaration;
}
public override AstNode VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data)
public override AstNode VisitMethodDeclaration(MethodDeclaration methodDeclaration)
{
return TransformDestructor(methodDeclaration) ?? base.VisitMethodDeclaration(methodDeclaration, data);
return TransformDestructor(methodDeclaration) ?? base.VisitMethodDeclaration(methodDeclaration);
}
public override AstNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement, object data)
public override AstNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement)
{
return TransformTryCatchFinally(tryCatchStatement) ?? base.VisitTryCatchStatement(tryCatchStatement, data);
return TransformTryCatchFinally(tryCatchStatement) ?? base.VisitTryCatchStatement(tryCatchStatement);
}
#endregion
@ -216,13 +220,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -216,13 +220,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
TryCatchStatement tryCatch = node.NextSibling as TryCatchStatement;
Match m2 = usingTryCatchPattern.Match(tryCatch);
if (!m2.Success) return null;
IL.ILVariable variable = m1.Get<IdentifierExpression>("variable").Single().GetILVariable();
string variableName = m1.Get<IdentifierExpression>("variable").Single().Identifier;
if (variableName != m2.Get<IdentifierExpression>("ident").Single().Identifier)
if (variable == null || variableName != m2.Get<IdentifierExpression>("ident").Single().Identifier)
return null;
if (m2.Has("valueType")) {
// if there's no if(x!=null), then it must be a value type
ILVariable v = m1.Get<AstNode>("variable").Single().Annotation<ILVariable>();
if (v == null || v.Type == null || !v.Type.IsValueType)
if (variable.Type.IsReferenceType != false)
return null;
}
@ -271,7 +275,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -271,7 +275,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
Name = variableName,
Initializer = m1.Get<Expression>("initializer").Single().Detach()
}.CopyAnnotationsFrom(node.Expression)
.WithAnnotation(m1.Get<AstNode>("variable").Single().Annotation<ILVariable>())
.WithAnnotation(variable)
}
}.CopyAnnotationsFrom(node);
} else {
@ -328,12 +332,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -328,12 +332,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
blocks.Reverse(); // go from parent blocks to child blocks
DefiniteAssignmentAnalysis daa = new DefiniteAssignmentAnalysis(blocks[0], context.CancellationToken);
declarationPoint = null;
foreach (BlockStatement block in blocks) {
return false;
/*foreach (BlockStatement block in blocks) {
if (!DeclareVariables.FindDeclarationPoint(daa, varDecl, block, out declarationPoint)) {
return false;
}
}
return true;
return true;*/
}
/// <summary>
@ -438,7 +443,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -438,7 +443,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
VariableName = itemVar.Identifier,
InExpression = m.Get<Expression>("collection").Single().Detach(),
EmbeddedStatement = newBody
}.WithAnnotation(itemVarDecl.Variables.Single().Annotation<ILVariable>());
}.WithAnnotation(itemVarDecl.Variables.Single().Annotation<IL.ILVariable>());
if (foreachStatement.InExpression is BaseReferenceExpression) {
foreachStatement.InExpression = new ThisReferenceExpression().CopyAnnotationsFrom(foreachStatement.InExpression);
}
@ -532,7 +537,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -532,7 +537,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{
VariableType = itemVarDecl.Type.Clone(),
VariableName = itemVar.Identifier,
}.WithAnnotation(itemVarDecl.Variables.Single().Annotation<ILVariable>());
}.WithAnnotation(itemVarDecl.Variables.Single().Annotation<IL.ILVariable>());
BlockStatement body = new BlockStatement();
foreachStatement.EmbeddedStatement = body;
((BlockStatement)node.Parent).Statements.InsertBefore(node, foreachStatement);
@ -628,7 +633,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -628,7 +633,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (m.Success) {
DoWhileStatement doLoop = new DoWhileStatement();
doLoop.Condition = new UnaryOperatorExpression(UnaryOperatorType.Not, m.Get<Expression>("condition").Single().Detach());
doLoop.Condition.AcceptVisitor(new PushNegation(), null);
//doLoop.Condition.AcceptVisitor(new PushNegation(), null);
BlockStatement block = (BlockStatement)whileLoop.EmbeddedStatement;
block.Statements.Last().Remove(); // remove if statement
doLoop.EmbeddedStatement = block.Detach();
@ -1069,7 +1074,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -1069,7 +1074,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
FieldDefinition field = eventDef.DeclaringType.Fields.FirstOrDefault(f => f.Name == ev.Name);
if (field != null) {
ed.AddAnnotation(field);
AstBuilder.ConvertAttributes(ed, field, "field");
// TODO AstBuilder.ConvertAttributes(ed, field, "field");
}
}
@ -1102,7 +1107,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -1102,7 +1107,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
methodDef.Attributes.MoveTo(dd.Attributes);
dd.Modifiers = methodDef.Modifiers & ~(Modifiers.Protected | Modifiers.Override);
dd.Body = m.Get<BlockStatement>("body").Single().Detach();
dd.Name = AstBuilder.CleanName(context.CurrentType.Name);
dd.Name = currentTypeDefinition.Name;
methodDef.ReplaceWith(dd);
return dd;
}

5
ICSharpCode.Decompiler/CSharp/Transforms/TransformContext.cs

@ -32,6 +32,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -32,6 +32,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public readonly DecompilerTypeSystem TypeSystem;
public readonly CancellationToken CancellationToken;
public readonly TypeSystemAstBuilder TypeSystemAstBuilder;
public readonly DecompilerSettings Settings;
readonly ITypeResolveContext decompilationContext;
/// <summary>
@ -48,11 +50,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -48,11 +50,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
get { return decompilationContext.CurrentTypeDefinition; }
}
internal TransformContext(DecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, TypeSystemAstBuilder typeSystemAstBuilder, CancellationToken cancellationToken)
internal TransformContext(DecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, TypeSystemAstBuilder typeSystemAstBuilder, DecompilerSettings settings, CancellationToken cancellationToken)
{
this.TypeSystem = typeSystem;
this.decompilationContext = decompilationContext;
this.TypeSystemAstBuilder = typeSystemAstBuilder;
this.Settings = settings;
this.CancellationToken = cancellationToken;
}
}

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -65,11 +65,13 @@ @@ -65,11 +65,13 @@
<ItemGroup>
<Compile Include="CSharp\Annotations.cs" />
<Compile Include="CSharp\DecompilerSettings.cs" />
<Compile Include="CSharp\Transforms\ContextTrackingVisitor.cs" />
<Compile Include="CSharp\Transforms\DecimalConstantTransform.cs" />
<Compile Include="CSharp\Transforms\DeclareVariables.cs" />
<Compile Include="CSharp\Transforms\EscapeInvalidIdentifiers.cs" />
<Compile Include="CSharp\Transforms\FixNameCollisions.cs" />
<Compile Include="CSharp\Transforms\IntroduceUsingDeclarations.cs" />
<Compile Include="CSharp\Transforms\PatternStatementTransform.cs" />
<Compile Include="CSharp\Transforms\TransformContext.cs" />
<Compile Include="CSharp\TranslatedExpression.cs" />
<Compile Include="CSharp\CSharpDecompiler.cs" />

12
ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs

@ -105,6 +105,12 @@ namespace ICSharpCode.Decompiler.IL @@ -105,6 +105,12 @@ namespace ICSharpCode.Decompiler.IL
return a;
}
/// <summary>
/// Gets whether this node (or any subnode) was modified since the last <c>ResetDirty()</c> call.
/// </summary>
/// <remarks>
/// IsDirty is used by the LoopingTransform, and must not be used by individual transforms within the loop.
/// </remarks>
public bool IsDirty { get; private set; }
protected void MakeDirty()
@ -113,6 +119,12 @@ namespace ICSharpCode.Decompiler.IL @@ -113,6 +119,12 @@ namespace ICSharpCode.Decompiler.IL
inst.IsDirty = true;
}
/// <summary>
/// Marks this node (and all subnodes) as <c>IsDirty=false</c>.
/// </summary>
/// <remarks>
/// IsDirty is used by the LoopingTransform, and must not be used by individual transforms within the loop.
/// </remarks>
public void ResetDirty()
{
foreach (ILInstruction inst in Descendants)

3
ICSharpCode.Decompiler/IL/Transforms/InlineCompilerGeneratedVariables.cs

@ -74,8 +74,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -74,8 +74,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (block == null)
return false;
ILInstruction next = block.Instructions.ElementAtOrDefault(stloc.ChildIndex + 1);
if (next == null)
continue; // store at end of block might still be a dead store
// NB: next==null is considerd as a dead store
if (ILInlining.CanInlineInto(next, v, stloc.Value)) {
loadsAccountedFor++;
storesToInline.Add(stloc);

3
ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs

@ -22,6 +22,9 @@ using System.Collections.Generic; @@ -22,6 +22,9 @@ using System.Collections.Generic;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>
/// Repeats the child transforms until the ILAst no longer changes.
/// </summary>
public class LoopingTransform : IILTransform
{
readonly IReadOnlyCollection<IILTransform> children;

1
ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -128,6 +128,7 @@ @@ -128,6 +128,7 @@
<Compile Include="TestCases\PropertiesAndEvents.cs" />
<Compile Include="TestCases\Switch.cs" />
<Compile Include="TestCases\UndocumentedExpressions.cs" />
<Compile Include="TestCases\UnsafeCode.cs" />
<Compile Include="TestCases\ValueTypeCall.cs" />
<Compile Include="TestRunner.cs" />
<Compile Include="Util\IntervalTests.cs" />

Loading…
Cancel
Save