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

Delegate currying refactorings #125

Merged
merged 8 commits into from
Aug 6, 2014
Merged

Delegate currying refactorings #125

merged 8 commits into from
Aug 6, 2014

Conversation

theoy
Copy link
Contributor

@theoy theoy commented Aug 6, 2014

For valid delegates, the MethodInfo returned by System.Delegate.Method does not necessarily match the signature of the delegate. For example, in .NET 3.5+, the C# compiler allows you to take extension methods such as:

int Enumerable.Count<T>(this IEnumerable en)

And bottle them up like so (assuming Enumerable was imported via a using):

var en = ...; // some IEnumerable
Func<int> counter = en.Count; // actually binds to Enumerable.Count

As you can see, even though the underlying Enumerable's public signature takes in a parameter, it's surprising that it can be captured by a delegate that does not accept parameters. Capturing "instance-like" invocations of extension methods is probably very uncommon for affected Moq scenarios (Callback(...), Returns(...), VerifySet(...)).

In addition, certain DLR code generation strategies also can cause a lambda to be emitted with an extra ignored "curried" parameter. As it turns out, invoking these signatures is noticeably faster (~33% time shavings off of invocation overhead). This is very likely because delegates already have to account for invoking instance methods - especially for the classic mainstream scenario of delegates invoking event handlers.

As it turns out, the next generation .NET Compiler Platform ("Roslyn") compilers are also exploring the use of delegate currying techniques.

These changes allow for Moq to be currying-aware in a backwards compatible way (with older compiler versions).

This fixes the failed Moq unit test cases when Moq/unit tests are compiled with Roslyn, as well as adds an extension method testcase to demonstrate the repro against pre-Roslyn C# compilers.

One lesser-known functionality in .NET delegates is the ability to curry the first parameter of a static function, as if it were an instance method.

There are a few known cases where this is used:

1. C# takes advantage of that when it allows for delegates to be created to "instance-like" invocations of extension methods.
2. The DLR also can choose to generate backing code for lambdas this way when the lambda doesn't close over other values, because CLR execution of the Invoke(...) method is noticeably faster (probably tuning for instance member invocations such as event handlers).
3. Roslyn (next generation .NET Compiler Platform) is exploring utilizing this technique on applicable lambdas, pending further analysis on compatibility. The usage case would be on lambdas that do not require capturing context ("static lambdas") - where it would shave ~33% off of delegate invocation overhead.
First step in moving new delegate currying-aware code to be part of shared extensions.
Now that the delegate currying is in an extension method, might as well make the code similar to before to minimize the code diff.
In some versions of the Roslyn compiler, lambda backing methods can have an extra parameter to speed up CLR delegate invocations.

We use the valueDel in a few ways, sometimes the sentinel value delegate (no-op) takes in no parameters. In this case, we need to update this to use the new helper method to see if the current value in valueDel accepts no parameters (rather than literally checking to see if its backing method has zero parameters)
Change a codeblock that tries to find the reflection parameter for the backing method of Action<T> by using the first parameter. In the case of curried delegates, there can be an extra parameter in front for the "logical instance". In that case, you actually can look at the last parameter instead.
kzu added a commit that referenced this pull request Aug 6, 2014
Delegate currying refactorings
@kzu kzu merged commit 694df01 into devlooped:master Aug 6, 2014
@kzu
Copy link
Contributor

kzu commented Aug 6, 2014

Shipped! https://www.nuget.org/packages/Moq

@theoy
Copy link
Contributor Author

theoy commented Aug 6, 2014

Awesome, thanks! ☺

From: Daniel Cazzulino [mailto:notifications@github.com]
Sent: Wednesday, August 6, 2014 1:19 PM
To: Moq/moq4
Cc: Theo Yaung
Subject: Re: [moq4] Delegate currying refactorings (#125)

Shipped! https://www.nuget.org/packages/Moq


Reply to this email directly or view it on GitHubhttps://github.com//pull/125#issuecomment-51391064.

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

Successfully merging this pull request may close these issues.

None yet

2 participants