Skip to content
This repository has been archived by the owner on Nov 1, 2020. It is now read-only.

Reflected constructor invocation fails when using null propagation operator in .NET Native #3565

Closed
activa opened this issue May 9, 2017 · 8 comments
Labels

Comments

@activa
Copy link

activa commented May 9, 2017

The following code works fine in any .NET runtime environment, including Mono, Xamarin, etc. :

    public class ABC {}
    // ...
    var constr = typeof(ABC).GetTypeInfo().DeclaredConstructors.First();

    var abc = (ABC) constr?.Invoke(new object[0]);
    // abc now contains an instance of ABC

On Windows UWP with .NET Native compilation enabled, the same code throws an exception of type NotImplementedException

However, when the null propagation operator is removed, it works perfectly on .NET Native:

    public class ABC {}
    // ...
    var constr = typeof(ABC).GetTypeInfo().DeclaredConstructors.First();

    var abc1 = (ABC) constr.Invoke(new object[0]);
    // abc1 now contains an instance of ABC

    // the following line throws an exception on .NET Native!
    // but it works fine on any other .NET runtime
    var abc2 = (ABC) constr?.Invoke(new object[0]);

The line in the stack trace where the exception occurs is:

at System.Reflection.ConstructorInfo.Invoke(Object[] parameters) 
in f:\dd\ndp\fxcore\CoreRT\src\System.Private.Reflection\src\System\Reflection\ConstructorInfo.cs:line 41

I asked the same question on Stack Overflow: http://stackoverflow.com/questions/43857106/net-native-code-crashes-on-constructor-invoke-null-propagation

@MichalStrehovsky
Copy link
Member

Can you give us more details so that we can route this?

  • What version of Visual Studio is this? (Help -> About in VS)
  • What CPU architecture you're compiling for?
  • What version of the Microsoft.NETCore.UniversalWindowsPlatform NuGet package is your project using?

I just tried this in VS and couldn't repro this failure, so I want to make sure we have everything to repro.

@activa
Copy link
Author

activa commented May 10, 2017

It's 100% reproducible on my end when creating an empty project, enabling .NET Native compilation and adding the code I posted here.

  • Microsoft.NETCore.UniversalWindowsPlatform version 5.2.3
  • CPU Architecture x86
  • Visual Studio 14.0.25431.01 Update 3
  • "Optimize Code" turned off (tried both, same result)

@MichalStrehovsky
Copy link
Member

Still no luck in reproing. This could be a bug in devirtualization. Code generation bugs are extremely finicky. Do you mind creating an ilcRepro file with your test project?

@activa
Copy link
Author

activa commented May 10, 2017

I will try to get you everything you need. Can I post the link to the ilcRepro here or do I need to send it by e-mail?

BTW, this issue is occurring on 3 different machines so it doesn't seem to be environment-related.

@MichalStrehovsky
Copy link
Member

E-mail is the proper channel for .NET Native for UWP apps, so taking it there wouldn't be a bad idea (I do get the dotnetnative at microsoft com emails and will be able to follow up).

We can close the issue here - the compiler used for .NET Native for UWP apps is different than the compiler we have in this repo. This will likely be a compiler bug that can't be fixed in this repo anyway.

@activa
Copy link
Author

activa commented May 10, 2017

Ok, I just thought that because the stacktrace clearly indicates that the Exception is thrown in the CoreRT code, that this would be the right place.

@activa
Copy link
Author

activa commented May 10, 2017

Repro file was created and I'll e-mail it to the e-mail address you mentioned.
It turns out that this issue only happens when the code is in a PCL (Profile151) referenced from the UWP project.

Reflection works so it's not caused by missing runtime directives (because the constructor was successfully found and the first line of code where the constructor is invoked without the ?. operator succeeds).

@MichalStrehovsky
Copy link
Member

Oh, okay, with Profile151 I have a repro and it's a rather annoying breaking change in the framework.

If you want to follow along:

  • ConstructorInfo.Invoke(object[]) method in the System.Reflection reference assembly (C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.5\Profile\Profile78\System.Reflection.dll says that the Invoke method is not virtual.
  • Somewhere someone decided that the method should be virtual and they changed it in the implementation. The reference assembly that the C# code compiles against was left untouched.
  • Normally this is not a big deal because C# pretty much always calls methods virtually (even if they're not virtual), because it needs the side effect of the virtual call (throw a NullReferenceException on null this).
  • Except with the null propagation operator the C# compiler knows that a NullReferenceException cannot occur and it decides to emit a normal call instruction instead of callvirt to prevent the unnecessary null check.
  • Doing a normal call to the ConstructorInfo.Invoke(object[]) method results in us landing in a method that should never be called.

The good news is that ConstructorInfo.Invoke(object[]) is no longer virtual as part of the NetStandard 2.0 compatibility effort (the previous link was to an old snapshot). That version of .NET Native hasn't shipped yet. The only workaround for now is not to let C# compiler optimize the callvirt to a call by avoiding the operator.

Cc @atsushikan

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants