CsWinRT icon indicating copy to clipboard operation
CsWinRT copied to clipboard

Handle scenarios when the transitive closure of types is known.

Open AaronRobinsonMSFT opened this issue 5 years ago • 12 comments

There are scenarios in WinRT where the transitive closure of all types is known. This would imply there are opportunities to optimize the RCW code generation logic and avoid querying the runtime for these types - see https://github.com/microsoft/CsWinRT/blob/4e73245f7465e3adf2ec4cbcfe0e54e9cc988786/cswinrt/strings/WinRT_Wrappers.cs#L121

Reasons for considering this are: AOT and linkability (reduce memory footprint).

AaronRobinsonMSFT avatar Feb 19 '20 02:02 AaronRobinsonMSFT

The default for the WinRT (store) apps should be the constrained environment, where the transitive closure of all types is known, without reflection fallbacks. Otherwise, the WinRT apps are going to be big and slow by default.

Would it make sense to have an explicit opt-in (e.g. API call) to enable the reflection fallbacks, so that only apps that want to pay for it get it?

jkotas avatar Feb 19 '20 02:02 jkotas

Another option would be to expose an API to allow an app to pre-register its factories. Then in cases where the transitive type closure is known, the entire table could be pre-registered at startup before the app runs.

We could make the reflection fallback opt-in if we feel that there are users who would rather have the code throw an exception than go down the reflection path.

jkoritzinsky avatar Feb 20 '20 01:02 jkoritzinsky

@jkoritzinsky Would CsWinRT expose this new API or the runtime for CsWinRT to query? I would prefer the former rather than the latter. I think providing some pseudo code that illustrates how Apps and CsWinRT would interact would be helpful to illustrate the idea.

AaronRobinsonMSFT avatar Feb 20 '20 01:02 AaronRobinsonMSFT

CsWinRT would expose this API and code generated by some task in the SDK toolchain would call it at startup either in a module initializer or a Main method. I’ll post some possible pseudo code tomorrow.

jkoritzinsky avatar Feb 20 '20 01:02 jkoritzinsky

Here's the API I'm thinking of. We can do either or both depending on what we think is better (or what gives better perf).

public partial static class ComWrappersSupport
{
     public static void RegisterObjectFactoryForRuntimeClass(string runtimeClassName, Func<WinRT.IInspectable, object> factory);

     public static void RegisterObjectFactoriesForRuntimeClass(params System.Collections.Generic.KeyValuePair<string, Func<WinRT.IInspectable, object>>[] factories);

}

The usage would be something like as follows:

public static void Main()
{
     WinRT.ComWrappersSupport.RegisterObjectFactoryForRuntimeClass("Windows.Foundation.IReference`1<Int32>", inspectable => new ABI.Windows.Foundation.IReference<int>(inspectable.As<ABI.Windows.Foundation.IReference<int>.Vftbl>()));
// Run the WinUI application here.
}

This would require us to make the types in the ABI namespace public instead of their current internal visibility.

jkoritzinsky avatar Feb 20 '20 19:02 jkoritzinsky

@jkoritzinsky Perhaps it would be better to have the caller provide a ComWrappers instance instead? I would like to limit ways to create external object wrappers and the ComWrappers already exists so it might make sense to reuse that.

AaronRobinsonMSFT avatar Feb 20 '20 19:02 AaronRobinsonMSFT

We could do that as well if we're ok with this API being available only on the .NET 5 target.

jkoritzinsky avatar Feb 20 '20 19:02 jkoritzinsky

Unless we want to play the type-fowarding game and define a ComWrappers type in CsWinRT in System.Runtime.InteropServices that type-forwards to the runtime-defined one on .NET 5.

jkoritzinsky avatar Feb 20 '20 19:02 jkoritzinsky

We could do that as well if we're ok with this API being available only on the .NET 5 target.

The non .NET 5 approach is already degraded due to the inability to properly interact with a tracker runtime (e.g. Jupiter) and the more expensive object identity and lifetime tracking mechanisms that are implemented in .NET Standard 2.0. I think that would be okay. I am not entirely convinced this API would provide the most optimized/AOT-friendly approach though.

AaronRobinsonMSFT avatar Feb 20 '20 19:02 AaronRobinsonMSFT

I meant define it in the System.Runtime.InteropServices namespace in a singular location (since some of the CsWinRT code is going to have to be shared across all projections anyway) that multitargets netstandard2.0 and net5 and type-forwards on the net5 target.

I don't particularly like the idea, but we could do it.

jkoritzinsky avatar Feb 20 '20 21:02 jkoritzinsky

Here's the API I'm thinking of

It would be nice if this API allows hydrating the registrations lazily. E.g. If the app has 1000 types but only one of them is actually created at runtime, the app should only pay for that one type.

jkotas avatar Feb 20 '20 21:02 jkotas

CsWinRT would expose this API and code generated by some task in the SDK toolchain would call it at startup either in a module initializer or a Main method. I’ll post some possible pseudo code tomorrow.

@jkoritzinsky is there work planned for the sdk toolchain to support this? And I'm guessing you are referring to the Windows SDK?

stevenbrix avatar Mar 03 '20 03:03 stevenbrix