Skip to content

Commit 54ddb25

Browse files
committed
Fixing use of blocks in figuring out if variable declarations and assignments can be joined
1 parent 5b7b3ad commit 54ddb25

File tree

2 files changed

+122
-26
lines changed

2 files changed

+122
-26
lines changed

ReadableExpressions.UnitTests/WhenFormattingCode.cs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
using System;
44
using System.Collections.Generic;
5+
using System.IO;
56
using System.Linq;
67
using System.Linq.Expressions;
78
using System.Reflection;
@@ -350,6 +351,106 @@ public void ShouldNotVarAssignAVariableAssignedInATryButUsedInACatch()
350351
Assert.AreEqual(EXPECTED.TrimStart(), translated);
351352
}
352353

354+
[TestMethod]
355+
public void ShouldVarAssignAVariableUsedInNestedConstructs()
356+
{
357+
var returnLabel = Expression.Label(typeof(long), "Return");
358+
var streamVariable = Expression.Variable(typeof(Stream), "stream");
359+
360+
var memoryStreamVariable = Expression.Variable(typeof(MemoryStream), "memoryStream");
361+
var streamAsMemoryStream = Expression.TypeAs(streamVariable, typeof(MemoryStream));
362+
var memoryStreamAssignment = Expression.Assign(memoryStreamVariable, streamAsMemoryStream);
363+
var nullMemoryStream = Expression.Default(memoryStreamVariable.Type);
364+
var memoryStreamNotNull = Expression.NotEqual(memoryStreamVariable, nullMemoryStream);
365+
var msLengthVariable = Expression.Variable(typeof(long), "msLength");
366+
var memoryStreamLength = Expression.Property(memoryStreamVariable, "Length");
367+
var msLengthAssignment = Expression.Assign(msLengthVariable, memoryStreamLength);
368+
369+
var msTryBlock = Expression.Block(new[] { msLengthVariable }, msLengthAssignment, msLengthVariable);
370+
var newNotSupportedException = Expression.New(typeof(NotSupportedException));
371+
var throwMsException = Expression.Throw(newNotSupportedException, typeof(long));
372+
var msCatchBlock = Expression.Catch(typeof(Exception), throwMsException);
373+
var memoryStreamTryCatch = Expression.TryCatch(msTryBlock, msCatchBlock);
374+
var returnMemoryStreamResult = Expression.Return(returnLabel, memoryStreamTryCatch);
375+
var ifMemoryStreamTryCatch = Expression.IfThen(memoryStreamNotNull, returnMemoryStreamResult);
376+
377+
var fileStreamVariable = Expression.Variable(typeof(FileStream), "fileStream");
378+
var streamAsFileStream = Expression.TypeAs(streamVariable, typeof(FileStream));
379+
var fileStreamAssignment = Expression.Assign(fileStreamVariable, streamAsFileStream);
380+
var nullFileStream = Expression.Default(fileStreamVariable.Type);
381+
var fileStreamNotNull = Expression.NotEqual(fileStreamVariable, nullFileStream);
382+
var fsLengthVariable = Expression.Variable(typeof(long), "fsLength");
383+
var fileStreamLength = Expression.Property(fileStreamVariable, "Length");
384+
var fsLengthAssignment = Expression.Assign(fsLengthVariable, fileStreamLength);
385+
386+
var fsTryBlock = Expression.Block(new[] { fsLengthVariable }, fsLengthAssignment, fsLengthVariable);
387+
var newIoException = Expression.New(typeof(IOException));
388+
var throwIoException = Expression.Throw(newIoException, typeof(long));
389+
var fsCatchBlock = Expression.Catch(typeof(Exception), throwIoException);
390+
var fileStreamTryCatch = Expression.TryCatch(fsTryBlock, fsCatchBlock);
391+
var returnFileStreamResult = Expression.Return(returnLabel, fileStreamTryCatch);
392+
var ifFileStreamTryCatch = Expression.IfThen(fileStreamNotNull, returnFileStreamResult);
393+
394+
var overallBlock = Expression.Block(
395+
new[] { memoryStreamVariable, fileStreamVariable },
396+
memoryStreamAssignment,
397+
ifMemoryStreamTryCatch,
398+
fileStreamAssignment,
399+
ifFileStreamTryCatch,
400+
Expression.Label(returnLabel, Expression.Constant(0L)));
401+
402+
var overallCatchBlock = Expression.Catch(typeof(Exception), Expression.Constant(-1L));
403+
var overallTryCatch = Expression.TryCatch(overallBlock, overallCatchBlock);
404+
405+
const string EXPECTED = @"
406+
try
407+
{
408+
var memoryStream = stream as MemoryStream;
409+
if (memoryStream != null)
410+
{
411+
return
412+
{
413+
try
414+
{
415+
var msLength = memoryStream.Length;
416+
return msLength;
417+
}
418+
catch
419+
{
420+
throw new NotSupportedException();
421+
}
422+
}
423+
}
424+
425+
var fileStream = stream as FileStream;
426+
if (fileStream != null)
427+
{
428+
return
429+
{
430+
try
431+
{
432+
var fsLength = fileStream.Length;
433+
return fsLength;
434+
}
435+
catch
436+
{
437+
throw new IOException();
438+
}
439+
}
440+
}
441+
442+
return 0L;
443+
}
444+
catch
445+
{
446+
return -1L;
447+
}";
448+
449+
var translated = overallTryCatch.ToReadableString();
450+
451+
Assert.AreEqual(EXPECTED.TrimStart(), translated);
452+
}
453+
353454
[TestMethod]
354455
public void ShouldTranslateAnExtensionExpressionType()
355456
{

ReadableExpressions/TranslationContext.cs

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@ private class ExpressionAnalysisVisitor : ExpressionVisitor
145145
private readonly List<ParameterExpression> _accessedVariables;
146146
private readonly List<Expression> _assignedAssignments;
147147
private readonly Stack<object> _constructs;
148-
private BlockExpression _currentBlock;
149148

150149
private ExpressionAnalysisVisitor()
151150
{
@@ -157,7 +156,6 @@ private ExpressionAnalysisVisitor()
157156
NamedLabelTargets = new List<LabelTarget>();
158157
ChainedMethodCalls = new List<MethodCallExpression>();
159158
_constructs = new Stack<object>();
160-
_currentBlock = null;
161159
}
162160

163161
#region Factory Method
@@ -208,48 +206,45 @@ private static Expression GetCoreExpression(Expression expression)
208206

209207
public List<MethodCallExpression> ChainedMethodCalls { get; }
210208

211-
protected override Expression VisitBlock(BlockExpression block)
212-
{
213-
_currentBlock = block;
214-
215-
return base.VisitBlock(block);
216-
}
217-
218209
protected override Expression VisitParameter(ParameterExpression variable)
219210
{
220211
if (VariableHasNotYetBeenAccessed(variable))
221212
{
222213
_accessedVariables.Add(variable);
223214
}
224-
else if (JoinedAssignedVariables.Contains(variable))
215+
216+
if (!JoinedAssignedVariables.Contains(variable))
225217
{
226-
var joinedAssignmentData = _constructsByAssignment
227-
.Where(kvp => kvp.Key.Left == variable)
228-
.Select(kvp => new
229-
{
230-
Assignment = kvp.Key,
231-
Construct = kvp.Value
232-
})
233-
.FirstOrDefault();
218+
return base.VisitParameter(variable);
219+
}
234220

235-
if ((joinedAssignmentData != null) && !_constructs.Contains(joinedAssignmentData.Construct))
221+
var joinedAssignmentData = _constructsByAssignment
222+
.Where(kvp => kvp.Key.Left == variable)
223+
.Select(kvp => new
236224
{
237-
// This variable was assigned within a construct but is being accessed
238-
// outside of that scope, so the assignment shouldn't be joined:
239-
JoinedAssignedVariables.Remove(variable);
240-
JoinedAssignments.Remove(joinedAssignmentData.Assignment);
241-
_constructsByAssignment.Remove(joinedAssignmentData.Assignment);
242-
}
225+
Assignment = kvp.Key,
226+
Construct = kvp.Value
227+
})
228+
.FirstOrDefault();
229+
230+
if ((joinedAssignmentData == null) || _constructs.Contains(joinedAssignmentData.Construct))
231+
{
232+
return base.VisitParameter(variable);
243233
}
244234

235+
// This variable was assigned within a construct but is being accessed
236+
// outside of that scope, so the assignment shouldn't be joined:
237+
JoinedAssignedVariables.Remove(variable);
238+
JoinedAssignments.Remove(joinedAssignmentData.Assignment);
239+
_constructsByAssignment.Remove(joinedAssignmentData.Assignment);
240+
245241
return base.VisitParameter(variable);
246242
}
247243

248244
protected override Expression VisitBinary(BinaryExpression binary)
249245
{
250246
if ((binary.NodeType == ExpressionType.Assign) &&
251247
(binary.Left.NodeType == ExpressionType.Parameter) &&
252-
((_currentBlock == null) || _currentBlock.Expressions.Contains(binary)) &&
253248
!JoinedAssignedVariables.Contains(binary.Left) &&
254249
!_assignedAssignments.Contains(binary))
255250
{

0 commit comments

Comments
 (0)