-
Notifications
You must be signed in to change notification settings - Fork 528
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
[trimming] preserve custom views and $(AndroidHttpClientHandlerType)
#8954
Changes from 3 commits
2163693
cebeb8a
ef77b4e
aef660c
7d84df7
02e0514
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
using Mono.Linker; | ||
using Mono.Linker.Steps; | ||
using Mono.Tuner; | ||
using Java.Interop.Tools.Cecil; | ||
using Xamarin.Android.Tasks; | ||
|
||
namespace MonoDroid.Tuner { | ||
|
@@ -16,9 +17,46 @@ public class MarkJavaObjects : BaseMarkHandler | |
public override void Initialize (LinkContext context, MarkContext markContext) | ||
{ | ||
base.Initialize (context, markContext); | ||
context.TryGetCustomData ("AndroidHttpClientHandlerType", out string androidHttpClientHandlerType); | ||
context.TryGetCustomData ("AndroidCustomViewMapFile", out string androidCustomViewMapFile); | ||
var customViewMap = MonoAndroidHelper.LoadCustomViewMapFile (androidCustomViewMapFile); | ||
|
||
markContext.RegisterMarkAssemblyAction (assembly => ProcessAssembly (assembly, androidHttpClientHandlerType, customViewMap)); | ||
markContext.RegisterMarkTypeAction (type => ProcessType (type)); | ||
} | ||
|
||
bool IsActiveFor (AssemblyDefinition assembly) | ||
{ | ||
return assembly.MainModule.HasTypeReference ("System.Net.Http.HttpMessageHandler") || assembly.MainModule.HasTypeReference ("Android.Util.IAttributeSet"); | ||
} | ||
|
||
public void ProcessAssembly (AssemblyDefinition assembly, string androidHttpClientHandlerType, Dictionary<string, HashSet<string>> customViewMap) | ||
{ | ||
if (!IsActiveFor (assembly)) | ||
return; | ||
|
||
foreach (var type in assembly.MainModule.Types) { | ||
// Custom HttpMessageHandler | ||
if (!string.IsNullOrEmpty (androidHttpClientHandlerType) && | ||
androidHttpClientHandlerType.StartsWith (type.Name, StringComparison.Ordinal)) { | ||
var assemblyQualifiedName = type.GetPartialAssemblyQualifiedName (Context); | ||
if (assemblyQualifiedName == androidHttpClientHandlerType) { | ||
Annotations.Mark (type); | ||
PreservePublicParameterlessConstructors (type); | ||
continue; | ||
} | ||
} | ||
|
||
// Custom views in Android .xml files | ||
jonathanpeppers marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (!type.ImplementsIJavaObject (cache)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
continue; | ||
if (customViewMap.ContainsKey (type.FullName)) { | ||
Annotations.Mark (type); | ||
PreserveJavaObjectImplementation (type); | ||
} | ||
} | ||
} | ||
|
||
public void ProcessType (TypeDefinition type) | ||
{ | ||
// If this isn't a JLO or IJavaObject implementer, | ||
|
@@ -48,6 +86,21 @@ void PreserveJavaObjectImplementation (TypeDefinition type) | |
PreserveInterfaces (type); | ||
} | ||
|
||
void PreservePublicParameterlessConstructors (TypeDefinition type) | ||
{ | ||
if (!type.HasMethods) | ||
return; | ||
|
||
foreach (var constructor in type.Methods) | ||
{ | ||
if (!constructor.IsConstructor || constructor.IsStatic || !constructor.IsPublic || constructor.HasParameters) | ||
continue; | ||
|
||
PreserveMethod (type, constructor); | ||
break; // We can stop when found | ||
} | ||
} | ||
|
||
void PreserveAttributeSetConstructor (TypeDefinition type) | ||
{ | ||
if (!type.HasMethods) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -408,19 +408,7 @@ public static Dictionary<string, HashSet<string>> LoadCustomViewMapFile (IBuildE | |
var cachedMap = engine?.GetRegisteredTaskObjectAssemblyLocal<Dictionary<string, HashSet<string>>> (mapFile, RegisteredTaskObjectLifetime.Build); | ||
if (cachedMap != null) | ||
return cachedMap; | ||
var map = new Dictionary<string, HashSet<string>> (); | ||
if (!File.Exists (mapFile)) | ||
return map; | ||
foreach (var s in File.ReadLines (mapFile)) { | ||
var items = s.Split (new char [] { ';' }, count: 2); | ||
var key = items [0]; | ||
var value = items [1]; | ||
HashSet<string> set; | ||
if (!map.TryGetValue (key, out set)) | ||
map.Add (key, set = new HashSet<string> ()); | ||
set.Add (value); | ||
} | ||
return map; | ||
return LoadCustomViewMapFile (mapFile); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we "register" var map = LoadCustomViewMapFile (mapFile);
if (map != null) {
engine.RegisterTaskObject (mapFile, map, RegisteredTaskObjectLifetime.Build, allowEarlyCollection: false);
}
return map; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The RegisterTaskObjectAssemblyLocall call happens inside There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh the code changed... ah cos of the linker can't use RegisterTaskObject There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry just actually read your comment (its early). The Registration of the file happens in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, the new method that doesn't support |
||
} | ||
|
||
public static bool SaveCustomViewMapFile (IBuildEngine4 engine, string mapFile, Dictionary<string, HashSet<string>> map) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,23 @@ | ||
using System.Diagnostics.CodeAnalysis; | ||
using Android.App; | ||
using Android.App; | ||
using Android.Content; | ||
using Android.Util; | ||
using Android.Views; | ||
using Android.Widget; | ||
using NUnit.Framework; | ||
using Mono.Android_Test.Library; | ||
|
||
namespace Xamarin.Android.RuntimeTests | ||
{ | ||
[TestFixture] | ||
public class CustomWidgetTests | ||
{ | ||
public CustomWidgetTests() | ||
{ | ||
// NOTE: ensure the C# compiler has a reference to the library | ||
new Mono.Android_Test.Library.Foo (); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was another problem, when we have a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #8904 is unrelated. The problem here is if you add a reference to a class library not using any types from it, and one of the I'm not sure if this would happen in practice/real customer projects. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I filed this for the future, and put it in comments: |
||
} | ||
|
||
// https://bugzilla.xamarin.com/show_bug.cgi?id=23880 | ||
[Test] | ||
[DynamicDependency (DynamicallyAccessedMemberTypes.All, typeof (CustomTextView))] | ||
public void UpperCaseCustomWidget_ShouldNotThrowInflateException () | ||
{ | ||
Assert.DoesNotThrow (() => { | ||
|
@@ -24,7 +27,6 @@ public void UpperCaseCustomWidget_ShouldNotThrowInflateException () | |
} | ||
|
||
[Test] | ||
[DynamicDependency (DynamicallyAccessedMemberTypes.All, typeof (CustomTextView))] | ||
public void LowerCaseCustomWidget_ShouldNotThrowInflateException () | ||
{ | ||
Assert.DoesNotThrow (() => { | ||
|
@@ -34,7 +36,6 @@ public void LowerCaseCustomWidget_ShouldNotThrowInflateException () | |
} | ||
|
||
[Test] | ||
[DynamicDependency (DynamicallyAccessedMemberTypes.All, typeof (CustomTextView))] | ||
public void UpperAndLowerCaseCustomWidget_FromLibrary_ShouldNotThrowInflateException () | ||
{ | ||
Assert.DoesNotThrow (() => { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
namespace Mono.Android_Test.Library; | ||
|
||
// NOTE: Type to be used, so there is a reference to this library | ||
public class Foo { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The point of this, is we don't want to iterate over every type in every assembly.
Is
Android.Util.IAttributeSet
required for any custom view?https://github.com/xamarin/xamarin-android/blob/14b70ac32b2a9efefae127795e96ef73124c8557/tests/Mono.Android-Tests/Mono.Android-Test.Library/CustomTextView.cs#L9
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See:
IAttributeSet
is used so that the View can obtain any XML attributes specified on the view. The above stack overflow answer has a good example of this:The
AttributeSet
is how theRectangleView
constructor would lookup the values for@app:radiusDimen
,@app:rectangleBackground
, and@app:circleBackground
.I believe, but cannot quickly verify, that the
(Context, IAttributeSet)
constructor is always used/preferred by Android.