Skip to content

Commit f21f51f

Browse files
authored
Avoiding discarding exception details of MissingMethodException in RuntimeType.CreateInstanceImpl (#108876)
1 parent f199591 commit f21f51f

File tree

4 files changed

+132
-5
lines changed

4 files changed

+132
-5
lines changed

src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3915,13 +3915,18 @@ private void CreateInstanceCheckThis()
39153915
}
39163916

39173917
MethodBase? invokeMethod;
3918-
object? state = null;
3918+
object? state;
39193919

39203920
try
39213921
{
39223922
invokeMethod = binder.BindToMethod(bindingAttr, cons, ref args, null, culture, null, out state);
39233923
}
3924-
catch (MissingMethodException) { invokeMethod = null; }
3924+
catch (MissingMethodException innerMME)
3925+
{
3926+
// Rethrows to rewrite a message to include the class name.
3927+
// Make sure the original exception is set as an inner exception.
3928+
throw new MissingMethodException(SR.Format(SR.MissingConstructor_Name, FullName), innerMME);
3929+
}
39253930

39263931
if (invokeMethod is null)
39273932
{

src/coreclr/nativeaot/System.Private.CoreLib/src/System/ActivatorImplementation.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,25 @@ public static object CreateInstance(
9898

9999
binder ??= Type.DefaultBinder;
100100

101-
MethodBase invokeMethod = binder.BindToMethod(bindingAttr, matches.ToArray(), ref args, null, culture, null, out object? state);
101+
MethodBase? invokeMethod;
102+
object? state;
103+
104+
try
105+
{
106+
invokeMethod = binder.BindToMethod(bindingAttr, matches.ToArray(), ref args, null, culture, null, out state);
107+
}
108+
catch (MissingMethodException innerMME)
109+
{
110+
// Rethrows to rewrite a message to include the class name.
111+
// Make sure the original exception is set as an inner exception.
112+
throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, type), innerMME);
113+
}
114+
115+
if (invokeMethod == null)
116+
{
117+
throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, type));
118+
}
119+
102120
if (invokeMethod.GetParametersAsSpan().Length == 0)
103121
{
104122
if (args.Length != 0)

src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ActivatorTests.cs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,105 @@ public void CreateInstance_PublicOnlyTypeWithPrivateDefaultConstructor_ThrowsMis
107107
Assert.Throws<MissingMethodException>(() => Activator.CreateInstance(typeof(TypeWithPrivateDefaultConstructor), nonPublic: false));
108108
}
109109

110+
[Fact]
111+
public void CreateInstance_WithCustomBinder_ThrowsMissingMethodException()
112+
{
113+
// MissingMethodException not caused by a binder must not contain an inner exception.
114+
var mme = Assert.Throws<MissingMethodException>(() => Activator.CreateInstance(typeof(TypeWithPrivateDefaultConstructor), nonPublic: false));
115+
Assert.Contains("System.Tests.ActivatorTests+TypeWithPrivateDefaultConstructor", mme.Message);
116+
Assert.Null(mme.InnerException);
117+
118+
mme = Assert.Throws<MissingMethodException>(() => Activator.CreateInstance(typeof(TypeWithoutDefaultCtor)));
119+
Assert.Contains("System.Tests.ActivatorTests+TypeWithoutDefaultCtor", mme.Message);
120+
Assert.Null(mme.InnerException);
121+
122+
// MissingMethodException caused by a binder must be wrapped.
123+
mme = Assert.Throws<MissingMethodException>(() => Activator.CreateInstance(
124+
typeof(object), BindingFlags.CreateInstance,
125+
new CustomBinder() { BindToMethodAction = () => throw new MissingMethodException("Hello, World!!") },
126+
null, null, null
127+
));
128+
Assert.Contains("System.Object", mme.Message);
129+
Assert.NotNull(mme.InnerException);
130+
Assert.IsType<MissingMethodException>(mme.InnerException);
131+
Assert.Equal("Hello, World!!", mme.InnerException.Message);
132+
133+
mme = Assert.Throws<MissingMethodException>(() => Activator.CreateInstance(
134+
typeof(Random), BindingFlags.CreateInstance,
135+
new CustomBinder() { BindToMethodAction = () => throw new MissingMethodException("good-bye...") },
136+
null, null, null
137+
));
138+
Assert.Contains("System.Random", mme.Message);
139+
Assert.NotNull(mme.InnerException);
140+
Assert.IsType<MissingMethodException>(mme.InnerException);
141+
Assert.Equal("good-bye...", mme.InnerException.Message);
142+
143+
// Any other exceptions will not be caught.
144+
Assert.Throws<Exception>(() => Activator.CreateInstance(
145+
typeof(object), BindingFlags.CreateInstance,
146+
new CustomBinder() { BindToMethodAction = () => throw new Exception() },
147+
null, null, null
148+
));
149+
Assert.Throws<ArgumentNullException>(() => Activator.CreateInstance(
150+
typeof(object), BindingFlags.CreateInstance,
151+
new CustomBinder() { BindToMethodAction = () => throw new ArgumentNullException() },
152+
null, null, null
153+
));
154+
Assert.Throws<FileNotFoundException>(() => Activator.CreateInstance(
155+
typeof(Random), BindingFlags.CreateInstance,
156+
new CustomBinder() { BindToMethodAction = () => throw new FileNotFoundException() },
157+
null, null, null
158+
));
159+
Assert.Throws<InvalidOperationException>(() => Activator.CreateInstance(
160+
typeof(Random), BindingFlags.CreateInstance,
161+
new CustomBinder() { BindToMethodAction = () => throw new InvalidOperationException() },
162+
null, null, null
163+
));
164+
165+
// MissingMethodException must not contain an inner exception when BindToMethod returns null.
166+
mme = Assert.Throws<MissingMethodException>(() => Activator.CreateInstance(
167+
typeof(object), BindingFlags.CreateInstance,
168+
new CustomBinder() { BindToMethodAction = () => null },
169+
null, null, null
170+
));
171+
Assert.Contains("System.Object", mme.Message);
172+
Assert.Null(mme.InnerException);
173+
174+
mme = Assert.Throws<MissingMethodException>(() => Activator.CreateInstance(
175+
typeof(Random), BindingFlags.CreateInstance,
176+
new CustomBinder() { BindToMethodAction = () => null },
177+
null, null, null
178+
));
179+
Assert.Contains("System.Random", mme.Message);
180+
Assert.Null(mme.InnerException);
181+
}
182+
183+
class CustomBinder : Binder
184+
{
185+
public required Func<MethodBase?> BindToMethodAction { get; init; }
186+
187+
public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object?[] args, ParameterModifier[]? modifiers, CultureInfo? culture, string[]? names, out object? state)
188+
{
189+
state = null;
190+
return this.BindToMethodAction()!;
191+
}
192+
193+
public override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo? culture)
194+
=> throw new NotImplementedException();
195+
196+
public override object ChangeType(object value, Type type, CultureInfo? culture)
197+
=> throw new NotImplementedException();
198+
199+
public override void ReorderArgumentArray(ref object?[] args, object state)
200+
=> throw new NotImplementedException();
201+
202+
public override MethodBase? SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[]? modifiers)
203+
=> throw new NotImplementedException();
204+
205+
public override PropertyInfo? SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type? returnType, Type[]? indexes, ParameterModifier[]? modifiers)
206+
=> throw new NotImplementedException();
207+
}
208+
110209
[Fact]
111210
public void CreateInstance_NullableType_ReturnsNull()
112211
{

src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,13 +1549,18 @@ private void CreateInstanceCheckThis()
15491549
}
15501550

15511551
MethodBase? invokeMethod;
1552-
object? state = null;
1552+
object? state;
15531553

15541554
try
15551555
{
15561556
invokeMethod = binder.BindToMethod(bindingAttr, cons, ref args, null, culture, null, out state);
15571557
}
1558-
catch (MissingMethodException) { invokeMethod = null; }
1558+
catch (MissingMethodException innerMME)
1559+
{
1560+
// Rethrows to rewrite a message to include the class name.
1561+
// Make sure the original exception is set as an inner exception.
1562+
throw new MissingMethodException(SR.Format(SR.MissingConstructor_Name, FullName), innerMME);
1563+
}
15591564

15601565
if (invokeMethod == null)
15611566
{

0 commit comments

Comments
 (0)