diff --git a/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs b/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs
index def1570be7975c..923ff3c00d4f92 100644
--- a/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs
+++ b/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs
@@ -104,17 +104,59 @@ private static bool HasCustomAttributeWithName(this MemberInfo memberInfo, strin
#if NET
return ctorInfo.Invoke(BindingFlags.DoNotWrapExceptions, null, parameters, null);
#else
- object? result = null;
try
{
- result = ctorInfo.Invoke(parameters);
+ return ctorInfo.Invoke(parameters);
}
- catch (TargetInvocationException ex)
+ catch (TargetInvocationException ex) when (ex.InnerException is not null)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
+ throw; // unreachable
}
+#endif
+ }
- return result;
+ ///
+ /// Invokes without wrapping any exception thrown by the
+ /// target method in a . This matches the behavior of
+ /// the Reflection.Emit-based accessor, which emits direct calls into user code.
+ ///
+ public static object? InvokeNoWrapExceptions(this MethodInfo methodInfo, object? obj, object?[]? parameters)
+ {
+#if NET
+ return methodInfo.Invoke(obj, BindingFlags.DoNotWrapExceptions, binder: null, parameters, culture: null);
+#else
+ try
+ {
+ return methodInfo.Invoke(obj, parameters);
+ }
+ catch (TargetInvocationException ex) when (ex.InnerException is not null)
+ {
+ ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
+ throw; // unreachable
+ }
+#endif
+ }
+
+ ///
+ /// Invokes without wrapping any exception thrown by the
+ /// constructor in a . This matches the behavior of
+ /// the Reflection.Emit-based accessor, which emits direct calls into user code.
+ ///
+ public static object InvokeNoWrapExceptions(this ConstructorInfo constructorInfo, object?[]? parameters)
+ {
+#if NET
+ return constructorInfo.Invoke(BindingFlags.DoNotWrapExceptions, binder: null, parameters, culture: null);
+#else
+ try
+ {
+ return constructorInfo.Invoke(parameters);
+ }
+ catch (TargetInvocationException ex) when (ex.InnerException is not null)
+ {
+ ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
+ throw; // unreachable
+ }
#endif
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/MemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/MemberAccessor.cs
index 248a560cd58ce6..9fbe468cebcced 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/MemberAccessor.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/MemberAccessor.cs
@@ -31,8 +31,13 @@ static MemberAccessor Initialize()
{
MemberAccessor value =
#if NET
- // if dynamic code isn't supported, fallback to reflection
- RuntimeFeature.IsDynamicCodeSupported ?
+ // On platforms where dynamic code is supported but not compiled to native code
+ // (e.g. the Mono interpreter, WASM and iOS), the IL emitted by the Reflection.Emit
+ // based accessor is only interpreted, offering no throughput benefit over plain
+ // reflection while still pulling in the Reflection.Emit stack. Gating on
+ // IsDynamicCodeCompiled (rather than IsDynamicCodeSupported) keeps Reflection.Emit
+ // on JIT-backed runtimes but lets the trimmer remove it everywhere else.
+ RuntimeFeature.IsDynamicCodeCompiled ?
new ReflectionEmitCachingMemberAccessor() :
new ReflectionMemberAccessor();
#elif NETFRAMEWORK
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs
index fd6f38ada0db7a..6cbe842f1085df 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs
@@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
+using System.Text.Json.Reflection;
namespace System.Text.Json.Serialization.Metadata
{
@@ -33,7 +34,7 @@ public ReflectionMemberAccessor()
: null;
}
- return () => ctorInfo.Invoke(null);
+ return () => ctorInfo.InvokeNoWrapExceptions(null);
}
public override Func