Skip to content

Commit 3f6546f

Browse files
committed
Prefer interpreted expression evaluation
1 parent 085dc47 commit 3f6546f

3 files changed

Lines changed: 6 additions & 18 deletions

File tree

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,4 +420,5 @@ async\method: Suffix with `Async` =\> `GetDataAsync()`
420420
- Byref-like expression trees (e.g., `Span<T>`, `ReadOnlySpan<T>`) cannot be boxed; convert them to arrays before evaluation to avoid interpreter type load errors.
421421
- Reflection helpers should tolerate missing overloads (e.g., `MemoryExtensions.ToArray`) to prevent type initializer failures on runtimes where a method shape differs.
422422
- Compilation can fail with `TypeLoadException`/`ArgumentException` in addition to `InvalidProgramException`; catch broadly and fall back to interpreted compilation or a safe null-returning delegate.
423+
- Prefer `Compile(preferInterpretation: true)` for expression value extraction to avoid invalid IL, and only drop to null when both interpreted compilation and span normalization fail.
423424
- `NUnit.Assert.ThrowsAsync<T>()` returns the exception instance (not a `Task`), so async tests should await a real async assertion (e.g., `action.Should().ThrowAsync<T>()`) to avoid CS1998 warnings.

learnings.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ This document is organized by topic to consolidate key learnings about the proje
131131
- **ByRef-like expression evaluation:** Ref structs (e.g., `Span<T>`, `ReadOnlySpan<T>`) cannot be boxed to `object`; convert them to arrays (e.g., `MemoryExtensions.ToArray`) before value extraction to avoid interpreter `TypeLoadException`/`ArgumentException`.
132132
- **Reflection resilience:** Do not assume specific overloads exist across runtimes; when reflecting for helpers like `MemoryExtensions.ToArray`, select via tolerant predicate and allow null fallback to avoid type initializer failures.
133133
- **Compilation fallback breadth:** Expression compilation can also fail with `TypeLoadException`/`ArgumentException` on byref-like conversions; wrap compilation and fall back to interpreted mode or a null-returning delegate when both paths fail.
134+
- **Interpretation-first evaluation:** Prefer `Lambda.Compile(preferInterpretation: true)` for expression value extraction to avoid invalid IL generation issues; only return null when both interpreted compilation and span-to-array normalization fail.
134135
- **NUnit Assert.ThrowsAsync return type:** `Assert.ThrowsAsync<T>()` returns the exception instance, not a `Task`, so async test methods need an explicit awaitable (e.g., `action.Should().ThrowAsync<T>()`) to avoid CS1998 warnings.
135136

136137
## String Diffing Implementation (Increment 5)

src/SharpAssert.Runtime/Features/Shared/ExpressionValueEvaluator.cs

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,11 @@ public static Func<object> Compile(Expression expression)
2121

2222
try
2323
{
24-
return lambda.Compile();
24+
return lambda.Compile(preferInterpretation: true);
2525
}
26-
catch (Exception ex) when (IsCompileFallbackCase(ex))
26+
catch
2727
{
28-
try
29-
{
30-
return lambda.Compile(preferInterpretation: true);
31-
}
32-
catch
33-
{
34-
return NullEvaluator;
35-
}
28+
return NullEvaluator;
3629
}
3730
}
3831

@@ -83,13 +76,6 @@ static Expression Normalize(Expression expression)
8376
return Expression.Convert(call, typeof(object));
8477
}
8578

86-
return Expression.Convert(expression, typeof(object));
87-
}
88-
89-
static bool IsCompileFallbackCase(Exception ex)
90-
{
91-
return ex is InvalidProgramException
92-
or TypeLoadException
93-
or ArgumentException;
79+
return Expression.Constant(null, typeof(object));
9480
}
9581
}

0 commit comments

Comments
 (0)