@@ -431,13 +431,15 @@ private object ResolveAndCache(int serviceTypeHash, Type serviceType, IfUnresolv
431
431
return value;
432
432
}
433
433
434
- // Important to cache expression first before trying to interpret, so that parallel resolutions may already use it.
435
- if (factory.CanCache)
436
- TryCacheDefaultFactory(serviceTypeHash, serviceType, expr);
437
-
438
434
// 1) First try to interpret
439
435
if (Interpreter.TryInterpretAndUnwrapContainerException(this, expr, out var instance))
436
+ {
437
+ // Nope. Important to cache expression first before trying to interpret, so that parallel resolutions may already use it.
438
+ // todo: @wip ...but what if exception is thrown, isn't it better to avoid caching the bad expression?
439
+ if (factory.CanCache)
440
+ TryCacheDefaultFactory(serviceTypeHash, serviceType, expr);
440
441
return instance;
442
+ }
441
443
442
444
// 2) Fallback to expression compilation
443
445
factoryDelegate = expr.CompileToFactoryDelegate(rules.UseInterpretation);
@@ -3028,10 +3030,35 @@ public static bool TryInterpretAndUnwrapContainerException(IResolverContext r, E
3028
3030
// Or if unsuccessful we may get the wrong exception, but it is even more unlikely case.
3029
3031
// It is unlikely in the first place because the majority of cases the scope access is not concurrent.
3030
3032
// Comparing to the singletons where it is expected to be concurrent, but it does not addressed here.
3033
+ TrySetScopedItemException(r, tex.InnerException);
3031
3034
throw tex.InnerException.TryRethrowWithPreservedStackTrace();
3032
3035
}
3033
3036
}
3034
3037
3038
+ private static void TrySetScopedItemException(IResolverContext r, Exception ex)
3039
+ {
3040
+ ScopedItemException itemEx = null;
3041
+ var s = r.CurrentScope as Scope;
3042
+ while (s != null)
3043
+ {
3044
+ var itemMaps = s.CloneMaps();
3045
+ foreach (var m in itemMaps)
3046
+ {
3047
+ if (!m.IsEmpty)
3048
+ foreach (var it in m.Enumerate())
3049
+ {
3050
+ if (it.Value == Scope.NoItem)
3051
+ {
3052
+ itemEx ??= new ScopedItemException(ex);
3053
+ if (Interlocked.CompareExchange(ref it.Value, itemEx, Scope.NoItem) == Scope.NoItem)
3054
+ return; // done
3055
+ }
3056
+ }
3057
+ }
3058
+ s = s.Parent as Scope;
3059
+ }
3060
+ }
3061
+
3035
3062
internal static bool TryInterpretSingletonAndUnwrapContainerException(IResolverContext r, Expression expr, ImMapEntry<object> itemRef, out object result)
3036
3063
{
3037
3064
try
@@ -3107,7 +3134,10 @@ public static bool TryInterpret(IResolverContext r, Expression expr,
3107
3134
}
3108
3135
case ExprType.Call:
3109
3136
{
3110
- return TryInterpretMethodCall(r, (MethodCallExpression)expr, paramExprs, paramValues, parentArgs, ref result);
3137
+ var ok = TryInterpretMethodCall(r, (MethodCallExpression)expr, paramExprs, paramValues, parentArgs, ref result);
3138
+ if (ok && result is ScopedItemException ie)
3139
+ ie.ReThrow();
3140
+ return ok;
3111
3141
}
3112
3142
case ExprType.Convert:
3113
3143
{
@@ -3118,6 +3148,8 @@ public static bool TryInterpret(IResolverContext r, Expression expr,
3118
3148
{
3119
3149
if (!TryInterpretMethodCall(r, m, paramExprs, paramValues, parentArgs, ref instance))
3120
3150
return false;
3151
+ if (instance is ScopedItemException ie)
3152
+ ie.ReThrow();
3121
3153
}
3122
3154
else if (!TryInterpret(r, operandExpr, paramExprs, paramValues, parentArgs, out instance))
3123
3155
return false;
@@ -12835,17 +12867,19 @@ public class Scope : IScope
12835
12867
12836
12868
internal static readonly object NoItem = new object();
12837
12869
12838
- private static ImMap<object>[] _emptySlots = CreateEmptyMaps();
12870
+ private static ImMap<object>[] _emptyMaps = CreateEmptyMaps();
12839
12871
12840
12872
private static ImMap<object>[] CreateEmptyMaps()
12841
12873
{
12842
- var slots = new ImMap<object>[MAP_COUNT];
12874
+ var maps = new ImMap<object>[MAP_COUNT];
12843
12875
var empty = ImMap<object>.Empty;
12844
12876
for (var i = 0; i < MAP_COUNT; ++i)
12845
- slots [i] = empty;
12846
- return slots ;
12877
+ maps [i] = empty;
12878
+ return maps ;
12847
12879
}
12848
12880
12881
+ internal ImMap<object>[] CloneMaps() => _maps.CopyNonEmpty();
12882
+
12849
12883
///<summary>Creating</summary>
12850
12884
[MethodImpl((MethodImplOptions)256)]
12851
12885
public static IScope Of(IScope parent, object name) =>
@@ -13140,7 +13174,7 @@ public void Dispose()
13140
13174
13141
13175
_disposables = ImMap<ImList<IDisposable>>.Empty; // todo: @perf @mem combine used and _factories together
13142
13176
_used = ImHashMap<Type, object>.Empty;
13143
- _maps = _emptySlots ;
13177
+ _maps = _emptyMaps ;
13144
13178
}
13145
13179
13146
13180
private static void SafelyDisposeOrderedDisposables(ImMap<ImList<IDisposable>> disposables)
0 commit comments