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

Make ParameterDefaultValue.TryGetDefaultValue bitcode compliant #47197

Closed
wants to merge 1 commit into from

Conversation

lukaswelte
Copy link

Context

When using this code in Xamarin apps targeting iOS or watchOS, the code failed when bitcode was enabled since a catch exception with a when block is not allowed/bitcode compliant.
In order to get this code working on bitcode enabled apps (all apps running in watchOS must have bitcode enabled), this PR removes the when and instead moves the condition into the catch block.

The same scenario also happened in other parts of the codebase. See mono/mono#19451 for example.

Example Stacktrace

This method contains IL not supported when compiled to bitcode.
 at Microsoft.Extensions.Internal.ParameterDefaultValue.TryGetDefaultValue (System.Reflection.ParameterInfo parameter, System.Object& defaultValue) <0x23bfb1c + 0x00078> in <84525b045edb404392d95a20a1ee4f64>:0 
  at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites (System.Type serviceType, System.Type implementationType, Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteChain callSiteChain, System.Reflection.ParameterInfo[] parameters, System.Boolean throwIfCallSiteNotFound) <0x23ce248 + 0x001d0> in <84525b045edb404392d95a20a1ee4f64>:0 
  at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite (Microsoft.Extensions.DependencyInjection.ServiceLookup.ResultCache lifetime, System.Type serviceType, System.Type implementationType, Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteChain callSiteChain) <0x23ccb20 + 0x00500> in <84525b045edb404392d95a20a1ee4f64>:0 
  at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateCallSite (System.Type serviceType, Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteChain callSiteChain) <0x23ca600 + 0x001ac> in <84525b045edb404392d95a20a1ee4f64>:0 
  at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory+<>c__DisplayClass7_0.<GetCallSite>b__0 (System.Type type) <0x23cf1c4 + 0x000ac> in <84525b045edb404392d95a20a1ee4f64>:0 
  at (wrapper delegate-invoke) System.Func`2[System.Type,Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceCallSite].invoke_TResult_T(System.Type)
  at System.Collections.Concurrent.ConcurrentDictionary`2[TKey,TValue].GetOrAdd (TKey_REF key, System.Func`2[T,TResult] valueFactory) <0x12b5f84 + 0x0036c> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.GetCallSite (System.Type serviceType, Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteChain callSite<…>
  at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) <0x106fbb0 + 0x00084> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.Task.ExecuteWithThreadLocal (System.Threading.Tasks.Task& currentTaskSlot) <0x109e3d8 + 0x00144> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.Task.ExecuteEntry (System.Boolean bPreventDoubleExecution) <0x1097494 + 0x00224> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem () <0x1097418 + 0x00070> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.ThreadPoolWorkQueue.Dispatch () <0x1078904 + 0x003c8> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at (wrapper delegate-invoke) System.Func`1[System.Boolean].invoke_TResult()
  at ObjCRuntime.Runtime.ThreadPoolDispatcher (System.Func`1[TResult] callback) <0x16ef750<…>
  at System.Threading.Tasks.UnwrapPromise`1[TResult].ProcessCompletedOuterTask (System.Threading.Tasks.Task task) <0x151afc8 + 0x0019c> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.UnwrapPromise`1[TResult].InvokeCore (System.Threading.Tasks.Task completingTask) <0x151acd8 + 0x00098> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.UnwrapPromise`1[TResult].Invoke (System.Threading.Tasks.Task completingTask) <0x151ab3c + 0x000e0> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.Task.FinishContinuations () <0x10998f0 + 0x01260> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.Task.FinishStageThree () <0x10963fc + 0x00088> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.Task.FinishStageTwo () <0x10960cc + 0x001e8> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.Task.Finish (System.Boolean bUserDelegateExecuted) <0x1095d94 + 0x000e4> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
<…>

When using this code in Xamarin apps targeting iOS or watchOS, the code failed when bitcode was enabled since a catch exception with a when block is not allowed/bitcode compliant.
In order to get this code working on bitcode enabled apps (all apps running in watchOS must have bitcode enabled), this PR removes the when and instead moves the condition into the catch block
@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

@dnfadmin
Copy link

dnfadmin commented Jan 19, 2021

CLA assistant check
All CLA requirements met.

@ghost
Copy link

ghost commented Jan 20, 2021

Tagging subscribers to this area: @eerhardt, @maryamariyan
See info in area-owners.md if you want to be subscribed.

Issue Details

Context

When using this code in Xamarin apps targeting iOS or watchOS, the code failed when bitcode was enabled since a catch exception with a when block is not allowed/bitcode compliant.
In order to get this code working on bitcode enabled apps (all apps running in watchOS must have bitcode enabled), this PR removes the when and instead moves the condition into the catch block.

The same scenario also happened in other parts of the codebase. See mono/mono#19451 for example.

Example Stacktrace

This method contains IL not supported when compiled to bitcode.
 at Microsoft.Extensions.Internal.ParameterDefaultValue.TryGetDefaultValue (System.Reflection.ParameterInfo parameter, System.Object& defaultValue) <0x23bfb1c + 0x00078> in <84525b045edb404392d95a20a1ee4f64>:0 
  at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites (System.Type serviceType, System.Type implementationType, Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteChain callSiteChain, System.Reflection.ParameterInfo[] parameters, System.Boolean throwIfCallSiteNotFound) <0x23ce248 + 0x001d0> in <84525b045edb404392d95a20a1ee4f64>:0 
  at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite (Microsoft.Extensions.DependencyInjection.ServiceLookup.ResultCache lifetime, System.Type serviceType, System.Type implementationType, Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteChain callSiteChain) <0x23ccb20 + 0x00500> in <84525b045edb404392d95a20a1ee4f64>:0 
  at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateCallSite (System.Type serviceType, Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteChain callSiteChain) <0x23ca600 + 0x001ac> in <84525b045edb404392d95a20a1ee4f64>:0 
  at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory+<>c__DisplayClass7_0.<GetCallSite>b__0 (System.Type type) <0x23cf1c4 + 0x000ac> in <84525b045edb404392d95a20a1ee4f64>:0 
  at (wrapper delegate-invoke) System.Func`2[System.Type,Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceCallSite].invoke_TResult_T(System.Type)
  at System.Collections.Concurrent.ConcurrentDictionary`2[TKey,TValue].GetOrAdd (TKey_REF key, System.Func`2[T,TResult] valueFactory) <0x12b5f84 + 0x0036c> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.GetCallSite (System.Type serviceType, Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteChain callSite<…>
  at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) <0x106fbb0 + 0x00084> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.Task.ExecuteWithThreadLocal (System.Threading.Tasks.Task& currentTaskSlot) <0x109e3d8 + 0x00144> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.Task.ExecuteEntry (System.Boolean bPreventDoubleExecution) <0x1097494 + 0x00224> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem () <0x1097418 + 0x00070> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.ThreadPoolWorkQueue.Dispatch () <0x1078904 + 0x003c8> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at (wrapper delegate-invoke) System.Func`1[System.Boolean].invoke_TResult()
  at ObjCRuntime.Runtime.ThreadPoolDispatcher (System.Func`1[TResult] callback) <0x16ef750<…>
  at System.Threading.Tasks.UnwrapPromise`1[TResult].ProcessCompletedOuterTask (System.Threading.Tasks.Task task) <0x151afc8 + 0x0019c> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.UnwrapPromise`1[TResult].InvokeCore (System.Threading.Tasks.Task completingTask) <0x151acd8 + 0x00098> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.UnwrapPromise`1[TResult].Invoke (System.Threading.Tasks.Task completingTask) <0x151ab3c + 0x000e0> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.Task.FinishContinuations () <0x10998f0 + 0x01260> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.Task.FinishStageThree () <0x10963fc + 0x00088> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.Task.FinishStageTwo () <0x10960cc + 0x001e8> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
  at System.Threading.Tasks.Task.Finish (System.Boolean bUserDelegateExecuted) <0x1095d94 + 0x000e4> in <94cd4c1255b24edb9c7f402e2ad54ddc>:0 
<…>
Author: lukaswelte
Assignees: -
Labels:

area-Extensions-DependencyInjection

Milestone: -

@eerhardt
Copy link
Member

This is valid C# code. Shouldn’t we be working on making this work in AOT? Other libraries are going to use this same pattern/feature. Do we expect them to change as well?

@MichalStrehovsky
Copy link
Member

The extra throw will cause debuggers to break when "Break on first chance exceptions" is enabled in the debugger UI - it makes the libraries less pleasant to debug.

This specific filter should probably be addressed as part of whatever is the solution to #46927 for platforms where a (performant) two pass exception handling semantic cannot be implemented because the underlying platform doesn't support it.

(Looks like the underlying issue that required this specific filter was fixed around .NET Core 3.1. Maybe the filter can be ifdeffed to only when compiling for netstandard 2.0?)

@akoeplinger
Copy link
Member

Maybe the filter can be ifdeffed to only when compiling for netstandard 2.0?

That wouldn't solve the issue for existing Xamarin apps though since they'll still use the netstandard2.0 build.

@eerhardt
Copy link
Member

@akoeplinger @marek-safar @vargaz - is this getting fixed in the runtime? It doesn't make sense to me to fix this assembly when any other assembly can use this same C# feature. Or some new change can add back a catch-when block to this library.

@vargaz
Copy link
Contributor

vargaz commented Feb 18, 2021

Right now on wasm we support this in aot+interpreter mode by falling back to the interpreter, but can't really support it in aot only mode.
In general exception filters are very hard/impossible to support with the correct semantics on some platforms like wasm. So it would be nice to limit their usage in the BCL at least.

@eerhardt
Copy link
Member

So it would be nice to limit their usage in the BCL at least.

If we want that, we should add a rule/analyzer to ensure people aren't using them.

cc @stephentoub @jkotas

@jkotas
Copy link
Member

jkotas commented Feb 19, 2021

The C# "when" keyword was specifically introduced to improve post-mortem diagnostic. The exception filters do not unwind the stack and so it is possible to get more information from the crash-dumps for unhandled exceptions that way. I believe that there was even a rule in some version of fxcop that was looking for places that should use exception filters, but are not. Undoing use of exception filters sounds like a step backward to me.

I understand that filters are hard to support with the correct semantics in current Mono AOT. I do not think it is impossible, it is just a lot of work that we do not want to do right now. I think falling back for interpreter for methods with filters is fine workaround for now.

@stephentoub
Copy link
Member

Undoing use of exception filters sounds like a step backward to me.

+1

@marek-safar
Copy link
Contributor

We should avoid using exceptions for control flow (it has a bad perf impact even without exception filters) and this one should be very easy to fix. The whole try/catch can be removed for netcore build because the workaround is needed for .net framework only.

dotnet/coreclr#17877

@eerhardt
Copy link
Member

The two assemblies which compile this file don't build for netcore. They only build for netstandard and net461.

<TargetFrameworks>netstandard2.1;netstandard2.0;net461</TargetFrameworks>

To remove the workaround we will have to add a TFM target for these assemblies.

Base automatically changed from master to main March 1, 2021 09:07
@maryamariyan
Copy link
Member

@eerhardt is it worth adding an extra configuration for fixing this? In my opinion it does not meet the bar to get extra asset in package and ApiCompat risk for the new added TFM.

But since this try/catch block is redundant for .net core, we could either wait until Xamarin also has pattern matching and then remove the block all together. Or we could add a TFM target to fix for .net framework, I would go with the former approach.

@jkotas
Copy link
Member

jkotas commented Mar 22, 2021

We are very likely to add configurations that target newer netcoreapp versions for most assemblies, to take advatange of new APIs in the core platform. If there is a reason to add target for newer runtime, just add it. I do not think it is worth it to try to be selective about it. Optimize for future, not for the past.

@eerhardt
Copy link
Member

Would we also need to add a newer netcoreapp version when we enable nullable annotations in these assemblies?

@maryamariyan
Copy link
Member

maryamariyan commented Mar 30, 2021

Closing this PR as per above discussion above.

What we want is to add $(NetCoreAppCurrent) TFM for these assemblies, since we don't want the try-catch workaround on new .NET Core runtimes. This way when building for .NET Core, we can just run the code with no try-catch.

The try-catch block will remain for older frameworks (i.e. netstandard).

Filed issue: #50439

@ghost ghost locked as resolved and limited conversation to collaborators Apr 29, 2021
@karelz karelz added this to the 6.0.0 milestone May 20, 2021
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.