Skip to content

Conversation

@radekdoulik
Copy link
Member

@radekdoulik radekdoulik commented Dec 18, 2020

Context: #5400
Context: dotnet/java-interop#761

Generate XAConfig.props file in Java.Interop to define XA_JI_EXCLUDE
compilation constant.

The generated file contains properties set for Java.Interop by XA. So
far it is only setting one property, and thus doesn't necessarily be
generated. I still wanted to be written by xaprep, plus it might be
handy in future for other configuration.

The currently set JavaInteropDefineConstants property is used
to disable ManagedPeer initialization.

We don't really use ManagedPeer today and it is duplicating
functionality of ConstructorBuilder class. The unwanted effect of
having it in JI is that it is pulling in System.Linq.Expressions, which
greatly increase assemblies size after linking.

The size reduction for simple XA app is cca 135kbytes of compressed
size.

Size difference in bytes ([*1] apk1 only, [*2] apk2 only):
  -          27 assemblies/Mono.Android.dll
  -          30 assemblies/System.Collections.Concurrent.dll
  -         989 assemblies/System.Linq.dll
  -       2,930 assemblies/Java.Interop.dll
  -       4,514 assemblies/System.Collections.dll *1
  -       4,874 assemblies/System.ObjectModel.dll *1
  -       7,987 assemblies/System.Private.CoreLib.dll
  -     115,284 assemblies/System.Linq.Expressions.dll *1
Summary:
  -     136,635 Assemblies -14.87% (of 918,689)
  -     139,506 Package size difference -1.79% (of 7,780,819)

jonpryor pushed a commit to dotnet/java-interop that referenced this pull request Jan 11, 2021
Context: dotnet/android#5400
Context: dotnet/android#5444

Don't call `ManagedPeer.Init()` from the `JniRuntime` constructor when
`XA_JI_EXCLUDE` is `#define`d.

The presence of `ManagedPeer.Init()` requires that *all*
Xamarin.Android apps contain `System.Linq.Expressions` types after
linking, because `ManagedPeer.Construct()` calls
`JniEnvironment.Runtime.MarshalMemberBuilder.CreateConstructActivationPeerFunc()`
which pulls in all the `System.Linq.Expressions` infrastructure.

Currently, *nothing* requires `ManagedPeer` *at all*; Java.Interop-
"style" "Java Activation" (8c83f64) is not used outside of unit tests.
Consequently, there is no need to pay the cost of
System.Linq.Expressions within all Xamarin.Android apps.

Wrap the `ManagedPeer.Init()` invocation with `XA_JI_EXCLUDE` so that
the Xamarin.Android build of `Java.Interop.dll` (893562c) *excludes*
the `ManagedPeer.Init()` invocation.  This allows every Xamarin.Android
app to link out System.Linq.Expressions, unless the app is otherwise
using that namespace.

`XA_JI_EXCLUDE` is intended to be set by
`$(JavaInteropDefineConstants)` MSBUild property within the optional
file `bin/Build$(Configuration)/XAConfig.props`, which the
xamarin-android build will create before building `Java.Interop.csproj`.

Cleanup the `src/Java.Interop` source code to remove use of the
`XA_INTEGRATION` symbol.  This symbol is no longer of use, and now
only adds noise.
@radekdoulik radekdoulik force-pushed the pr-disable-managed-peer-init branch from d28a78c to fa92b24 Compare January 12, 2021 13:54
@radekdoulik radekdoulik marked this pull request as ready for review January 12, 2021 14:18
Context: dotnet#5400
Context: dotnet/java-interop#761

Generate `XAConfig.props` file in Java.Interop to define `XA_JI_EXCLUDE`
compilation constant.

The generated file contains properties set for Java.Interop by XA. So
far it is only setting one property, and thus doesn't necessarily be
generated. I still wanted to be written by `xaprep`, plus it might be
handy in future for other configuration.

The currently set `JavaInteropDefineConstants` property is going to be
used to disable `ManagedPeer` initialization.

We don't really use `ManagedPeer` today and it is duplicating
functionality of `ConstructorBuilder` class. The unwanted effect of
having it in JI is that it is pulling in System.Linq.Expressions, which
greatly increase assemblies size after linking.

The size reduction for simple XA app is cca 135kbytes of compressed
size.

    Size difference in bytes ([*1] apk1 only, [*2] apk2 only):
      -          27 assemblies/Mono.Android.dll
      -          30 assemblies/System.Collections.Concurrent.dll
      -         989 assemblies/System.Linq.dll
      -       2,930 assemblies/Java.Interop.dll
      -       4,514 assemblies/System.Collections.dll *1
      -       4,874 assemblies/System.ObjectModel.dll *1
      -       7,987 assemblies/System.Private.CoreLib.dll
      -     115,284 assemblies/System.Linq.Expressions.dll *1
    Summary:
      -     136,635 Assemblies -14.87% (of 918,689)
      -     139,506 Package size difference -1.79% (of 7,780,819)
@radekdoulik radekdoulik force-pushed the pr-disable-managed-peer-init branch from 095fc6e to a04ba26 Compare January 15, 2021 10:27
@jonpryor
Copy link
Contributor

Current Mono.Android-Tests on-device .apk execution fails:

01-15 11:35:33.224  3468  3532 E mono    : Java.Lang.LinkageError: No implementation found for void com.xamarin.java_interop.ManagedPeer.construct(java.lang.Object, java.lang.String, java.lang.String, java.lang.Object[]) (tried Java_com_xamarin_java_1interop_ManagedPeer_construct and Java_com_xamarin_java_1interop_ManagedPeer_construct__Ljava_lang_Object_2Ljava_lang_String_2Ljava_lang_String_2_3Ljava_lang_Object_2)

This happens because commit 130905e added the Java.Interop unit tests to the Mono.Android-Tests app, and the Java.Interop unit tests do use ManagedPeer.

There are thus two paths forward:

  1. "Partially revert" 130905e and stop running at least some of the Java.Interop unit tests under Xamarin.Android, or
  2. Re-introduce ManagedPeer, and find a different way of removing the System.Linq.Expressions.dll usage.

I'm of course partial to (2).

How do we remove the System.Linq.Expressions.dll usage? An idea is to:

  1. Add a JniRuntime.JniValueManager.ActivatePeer() method
  2. Update ManagedPeer.Construct() to call ActivatePeer()
  3. "Appropriate glue" to make it all work.

JniRuntime.JniValueManager.ActivatePeer(), in JniRuntime.JniValueManager could resemble:

namespace Java.Interop {
    partial class JniRuntime {
        partial abstract class JniValueManager {
            public abstract void ActivatePeer (JniObjectReference reference, ConstructorInfo peerConstructor, object[]? constructorArgumentValues);
        }
    }
}

Within the Java.Interop repo, MonoRuntimeValueManager.cs would need to be updated to override JniRuntime.JniValueManager.ActivatePeer(), and would contain what is currently in ManagedPeer.Construct() lines 107-120. (Note: LINQ use should be removed from lines 96-98.)

AndroidValueManager will need to be similarly updated, but would instead implement JniValueManager.ActivatePeer() by using TypeManager.n_Activate().

Once JniValueManager.ActivatePeer() exists, ManagedPeer.Construct() can be updated to remove ManagedPeer.Construct() lines 107-120, thus removing the System.Linq.Expressions usage from that type, and the JreRuntime constructor can remove the #if XA_JI_EXCLUDE block. Lines 107-120 would be replaced with:

JniEnvironment.Runtime.ValueManager.ActivatePeer (new JniObjectReference (n_self), ctor, pvalues);

This should allow ManagedPeer to be usable on Xamarin.Android, without pulling in the large System.Linq.Expressions dependency.

radekdoulik added a commit that referenced this pull request Jan 21, 2021
Context: #5444

This allows us to remove SLE dependency and save cca 15% of assembly
size.

Size difference of BuildReleaseArm64False on net6:

    Size difference in bytes ([*1] apk1 only, [*2] apk2 only):
      +          96 assemblies/Mono.Android.dll
      -         331 assemblies/System.Collections.Concurrent.dll
      -         907 assemblies/Java.Interop.dll
      -       1,003 assemblies/System.Linq.dll
      -       3,856 assemblies/System.ObjectModel.dll *1
      -       4,496 assemblies/System.Collections.dll *1
      -       7,824 assemblies/System.Private.CoreLib.dll
      -     115,284 assemblies/System.Linq.Expressions.dll *1
    Summary:
      -     133,605 Assemblies -15.33% (of 871,776)
@radekdoulik
Copy link
Member Author

Superseded by #5530

radekdoulik added a commit to radekdoulik/xamarin-android that referenced this pull request Jan 22, 2021
Context: dotnet#5444

This allows us to remove SLE dependency and save cca 15% of assembly
size.

Size difference of BuildReleaseArm64False on net6:

    Size difference in bytes ([*1] apk1 only, [*2] apk2 only):
      +          96 assemblies/Mono.Android.dll
      -         331 assemblies/System.Collections.Concurrent.dll
      -         907 assemblies/Java.Interop.dll
      -       1,003 assemblies/System.Linq.dll
      -       3,856 assemblies/System.ObjectModel.dll *1
      -       4,496 assemblies/System.Collections.dll *1
      -       7,824 assemblies/System.Private.CoreLib.dll
      -     115,284 assemblies/System.Linq.Expressions.dll *1
    Summary:
      -     133,605 Assemblies -15.33% (of 871,776)
@github-actions github-actions bot locked and limited conversation to collaborators Jan 26, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants