Skip to content

Commit

Permalink
Avoiding discarding exception details of MissingMethodException in …
Browse files Browse the repository at this point in the history
…`RuntimeType.CreateInstanceImpl` (dotnet#108876)
  • Loading branch information
Takym authored Oct 24, 2024
1 parent f199591 commit f21f51f
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3915,13 +3915,18 @@ private void CreateInstanceCheckThis()
}

MethodBase? invokeMethod;
object? state = null;
object? state;

try
{
invokeMethod = binder.BindToMethod(bindingAttr, cons, ref args, null, culture, null, out state);
}
catch (MissingMethodException) { invokeMethod = null; }
catch (MissingMethodException innerMME)
{
// Rethrows to rewrite a message to include the class name.
// Make sure the original exception is set as an inner exception.
throw new MissingMethodException(SR.Format(SR.MissingConstructor_Name, FullName), innerMME);
}

if (invokeMethod is null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,25 @@ public static object CreateInstance(

binder ??= Type.DefaultBinder;

MethodBase invokeMethod = binder.BindToMethod(bindingAttr, matches.ToArray(), ref args, null, culture, null, out object? state);
MethodBase? invokeMethod;
object? state;

try
{
invokeMethod = binder.BindToMethod(bindingAttr, matches.ToArray(), ref args, null, culture, null, out state);
}
catch (MissingMethodException innerMME)
{
// Rethrows to rewrite a message to include the class name.
// Make sure the original exception is set as an inner exception.
throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, type), innerMME);
}

if (invokeMethod == null)
{
throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, type));
}

if (invokeMethod.GetParametersAsSpan().Length == 0)
{
if (args.Length != 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,105 @@ public void CreateInstance_PublicOnlyTypeWithPrivateDefaultConstructor_ThrowsMis
Assert.Throws<MissingMethodException>(() => Activator.CreateInstance(typeof(TypeWithPrivateDefaultConstructor), nonPublic: false));
}

[Fact]
public void CreateInstance_WithCustomBinder_ThrowsMissingMethodException()
{
// MissingMethodException not caused by a binder must not contain an inner exception.
var mme = Assert.Throws<MissingMethodException>(() => Activator.CreateInstance(typeof(TypeWithPrivateDefaultConstructor), nonPublic: false));
Assert.Contains("System.Tests.ActivatorTests+TypeWithPrivateDefaultConstructor", mme.Message);
Assert.Null(mme.InnerException);

mme = Assert.Throws<MissingMethodException>(() => Activator.CreateInstance(typeof(TypeWithoutDefaultCtor)));
Assert.Contains("System.Tests.ActivatorTests+TypeWithoutDefaultCtor", mme.Message);
Assert.Null(mme.InnerException);

// MissingMethodException caused by a binder must be wrapped.
mme = Assert.Throws<MissingMethodException>(() => Activator.CreateInstance(
typeof(object), BindingFlags.CreateInstance,
new CustomBinder() { BindToMethodAction = () => throw new MissingMethodException("Hello, World!!") },
null, null, null
));
Assert.Contains("System.Object", mme.Message);
Assert.NotNull(mme.InnerException);
Assert.IsType<MissingMethodException>(mme.InnerException);
Assert.Equal("Hello, World!!", mme.InnerException.Message);

mme = Assert.Throws<MissingMethodException>(() => Activator.CreateInstance(
typeof(Random), BindingFlags.CreateInstance,
new CustomBinder() { BindToMethodAction = () => throw new MissingMethodException("good-bye...") },
null, null, null
));
Assert.Contains("System.Random", mme.Message);
Assert.NotNull(mme.InnerException);
Assert.IsType<MissingMethodException>(mme.InnerException);
Assert.Equal("good-bye...", mme.InnerException.Message);

// Any other exceptions will not be caught.
Assert.Throws<Exception>(() => Activator.CreateInstance(
typeof(object), BindingFlags.CreateInstance,
new CustomBinder() { BindToMethodAction = () => throw new Exception() },
null, null, null
));
Assert.Throws<ArgumentNullException>(() => Activator.CreateInstance(
typeof(object), BindingFlags.CreateInstance,
new CustomBinder() { BindToMethodAction = () => throw new ArgumentNullException() },
null, null, null
));
Assert.Throws<FileNotFoundException>(() => Activator.CreateInstance(
typeof(Random), BindingFlags.CreateInstance,
new CustomBinder() { BindToMethodAction = () => throw new FileNotFoundException() },
null, null, null
));
Assert.Throws<InvalidOperationException>(() => Activator.CreateInstance(
typeof(Random), BindingFlags.CreateInstance,
new CustomBinder() { BindToMethodAction = () => throw new InvalidOperationException() },
null, null, null
));

// MissingMethodException must not contain an inner exception when BindToMethod returns null.
mme = Assert.Throws<MissingMethodException>(() => Activator.CreateInstance(
typeof(object), BindingFlags.CreateInstance,
new CustomBinder() { BindToMethodAction = () => null },
null, null, null
));
Assert.Contains("System.Object", mme.Message);
Assert.Null(mme.InnerException);

mme = Assert.Throws<MissingMethodException>(() => Activator.CreateInstance(
typeof(Random), BindingFlags.CreateInstance,
new CustomBinder() { BindToMethodAction = () => null },
null, null, null
));
Assert.Contains("System.Random", mme.Message);
Assert.Null(mme.InnerException);
}

class CustomBinder : Binder
{
public required Func<MethodBase?> BindToMethodAction { get; init; }

public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object?[] args, ParameterModifier[]? modifiers, CultureInfo? culture, string[]? names, out object? state)
{
state = null;
return this.BindToMethodAction()!;
}

public override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo? culture)
=> throw new NotImplementedException();

public override object ChangeType(object value, Type type, CultureInfo? culture)
=> throw new NotImplementedException();

public override void ReorderArgumentArray(ref object?[] args, object state)
=> throw new NotImplementedException();

public override MethodBase? SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[]? modifiers)
=> throw new NotImplementedException();

public override PropertyInfo? SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type? returnType, Type[]? indexes, ParameterModifier[]? modifiers)
=> throw new NotImplementedException();
}

[Fact]
public void CreateInstance_NullableType_ReturnsNull()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1549,13 +1549,18 @@ private void CreateInstanceCheckThis()
}

MethodBase? invokeMethod;
object? state = null;
object? state;

try
{
invokeMethod = binder.BindToMethod(bindingAttr, cons, ref args, null, culture, null, out state);
}
catch (MissingMethodException) { invokeMethod = null; }
catch (MissingMethodException innerMME)
{
// Rethrows to rewrite a message to include the class name.
// Make sure the original exception is set as an inner exception.
throw new MissingMethodException(SR.Format(SR.MissingConstructor_Name, FullName), innerMME);
}

if (invokeMethod == null)
{
Expand Down

0 comments on commit f21f51f

Please sign in to comment.