Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve unit test coverage for GetUninitializedObject #44843

Merged
merged 4 commits into from
Nov 18, 2020

Conversation

GrabYourPitchforks
Copy link
Member

Based on a comment at #32520 (comment) that we should improve unit test coverage for RuntimeHelpers.GetUninitializedObject before making any changes to the runtime logic. This helps demonstrate that these unit tests pass both before + after any changes to the implementation.

@GrabYourPitchforks
Copy link
Member Author

Note - there are two test failures on my machine (CoreCLR Win10).

  • GetUninitializedObject(typeof(Action)) returns a zero-inited Action object rather than throwing.
  • GetUninitializedObject(typeof(void)) returns a boxed System.Void instance.

I think we can live with the zero-inited Action. But the boxed System.Void is really peculiar and should probably be blocked?

@@ -192,16 +191,102 @@ private static void FillStack(int depth)
}
}

public static IEnumerable<object[]> GetUninitializedObject_NegativeTestCases()
{
// TODO: Test actual function pointer types when typeof(delegate*<...>) support is available
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this TODO still relevant?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. typeof(delegate*<void>) is basically treated the same as typeof(IntPtr), so GetUninitializedObject(typeof(delegate*<void>)) succeeds since it results in a zero-inited boxed IntPtr. Presumably once the runtime and reflection stack treat these as real pointer types then we'll want to block them, same as void* today.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add this as an item to the issue about better support for pointers in reflection.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add this as an item to the issue about better support for pointers in reflection.

Already called out in the top post of #11354.

@GrabYourPitchforks GrabYourPitchforks removed the test-enhancement Improvements of test source code label Nov 18, 2020
@GrabYourPitchforks
Copy link
Member Author

GrabYourPitchforks commented Nov 18, 2020

Latest iteration: respond to PR feedback, plus block RuntimeHelpers.GetUninitializedObject(typeof(void)), which could be used to create boxed voids.

If somebody wants to hack the runtime into creating boxed voids, that's on them, I guess. But it seemed prudent to block it here since GetUninitializedObject will likely become the workhorse behind Activator.CreateInstance and related APIs.

Copy link
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@GrabYourPitchforks
Copy link
Member Author

GrabYourPitchforks commented Nov 18, 2020

Latest iterations are getting these tests to pass on mono. The mono runtime previously allowed calling GetUninitializedObject(typeof(int[])) to create uninitialized variable-length types, etc. This PR is unifying the behaviors so that all runtimes forbid calling GetUninitializedObject(...) with the following types.

  • abstract types and interfaces
  • variable-length types (arrays and strings)
  • pointers, byrefs, and byref-like types
  • open generic type definitions, generic parameter placeholders or constraints, and other "meta" types
  • System.Void
  • System.Nullable<T> (will be treated as simply T)
  • COM proxies

@jkotas
Copy link
Member

jkotas commented Nov 18, 2020

cc @CoffeeFlux for the Mono changes.

yield return new[] { typeof(IDisposable), typeof(MemberAccessException) }; // interface type

yield return new[] { typeof(List<>), typeof(MemberAccessException) }; // open generic type
yield return new[] { typeof(List<>).GetGenericArguments()[0], PlatformDetection.IsMonoRuntime ? typeof(MemberAccessException) : typeof(ArgumentException) }; // 'T' placeholder typedesc
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this difference something that's worth fixing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly I'd prefer to normalize absolutely everything to ArgumentException, but figured there might be people relying on the old behavior.

Copy link
Member Author

@GrabYourPitchforks GrabYourPitchforks Nov 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out Activator.CreateInstance and RuntimeHelpers.GetUninitializedObject perform many of the same checks, but they throw different exceptions. For example, when given an abstract class, CreateInstance throws MissingMethodException, but GetUninitializedObject throws MemberAccessException. Unifying these is going to be a bear.

Since the Activator.CreateInstance exceptions are documented on MSDN but the RuntimeHelpers.GetUninitializedObject exceptions aren't, I'm thinking of unifying on the Activator.CreateInstance behavior and taking the break in GetUninitializedObject. Thoughts?

Copy link
Member

@lambdageek lambdageek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mono changes LGTM

@GrabYourPitchforks GrabYourPitchforks merged commit 2d80101 into dotnet:master Nov 18, 2020
@GrabYourPitchforks GrabYourPitchforks deleted the aci_tests branch November 18, 2020 17:38
akoeplinger pushed a commit to mono/mono that referenced this pull request Nov 18, 2020
Copy dotnet/runtime#44843

Co-authored-by: GrabYourPitchforks <GrabYourPitchforks@users.noreply.github.com>
@ghost ghost locked as resolved and limited conversation to collaborators Dec 19, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants