Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions Documentation/guides/building-apps/build-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,27 @@ used in Android Application projects. The default value is
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
```

## AndroidLinkResources

When `true` this will make the build system link out the Nested Types
of the Resource.Designer.cs `Resource` class in all assemblies. The
IL code that uses those types will be updated to use the values
directly rather than accessing fields.

This can have a small impact on reducing the apk size, it might also
help slightly with startup performance. This will only effect "Release"
based builds.

***Experimental***. Only designed to work with code such as

```
var view = FindViewById(Resources.Ids.foo);
```

Any other scenarios (such as reflection) will not be supported.

Added in Xamarin.Android 11.3.

## AndroidLinkSkip

Specifies a semicolon-delimited (`;`)
Expand Down
5 changes: 5 additions & 0 deletions src/Microsoft.Android.Sdk.ILLink/SetupStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ protected override void Process ()
if (Context.TryGetCustomData ("AddKeepAlivesStep", out addKeepAlivesStep) && bool.TryParse (addKeepAlivesStep, out var bv) && bv)
InsertAfter (new AddKeepAlivesStep (cache), "CleanStep");

string androidLinkResources;
if (Context.TryGetCustomData ("AndroidLinkResources", out androidLinkResources) && bool.TryParse (androidLinkResources, out var linkResources) && linkResources) {
InsertAfter (new RemoveResourceDesignerStep (), "CleanStep");
InsertAfter (new GetAssembliesStep (), "CleanStep");
}
InsertAfter (new StripEmbeddedLibraries (), "CleanStep");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Mono.Cecil;
using Mono.Linker;
using Mono.Linker.Steps;
using System;
using System.Linq;
using Xamarin.Android.Tasks;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Mono.Cecil.Cil;

namespace MonoDroid.Tuner
{
public class AndroidLinkConfiguration {
public List<AssemblyDefinition> Assemblies { get; private set; } = new List<AssemblyDefinition> ();

static ConditionalWeakTable<LinkContext, AndroidLinkConfiguration> configurations = new ConditionalWeakTable<LinkContext, AndroidLinkConfiguration> ();

public static AndroidLinkConfiguration GetInstance (LinkContext context)
{
if (!configurations.TryGetValue (context, out AndroidLinkConfiguration config)) {
config = new AndroidLinkConfiguration ();
configurations.Add (context, config);
}
return config;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;

using Mono.Cecil;
using Mono.Cecil.Cil;

using Mono.Linker;

Expand Down Expand Up @@ -327,5 +328,39 @@ public static bool TryGetMarshalMethod (this MethodDefinition method, string nat

return false;
}

public static Instruction CreateLoadArraySizeOrOffsetInstruction (int intValue)
{
if (intValue < 0)
throw new ArgumentException ($"{nameof (intValue)} cannot be negative");

if (intValue < 9) {
switch (intValue) {
case 0:
return Instruction.Create (OpCodes.Ldc_I4_0);
case 1:
return Instruction.Create (OpCodes.Ldc_I4_1);
case 2:
return Instruction.Create (OpCodes.Ldc_I4_2);
case 3:
return Instruction.Create (OpCodes.Ldc_I4_3);
case 4:
return Instruction.Create (OpCodes.Ldc_I4_4);
case 5:
return Instruction.Create (OpCodes.Ldc_I4_5);
case 6:
return Instruction.Create (OpCodes.Ldc_I4_6);
case 7:
return Instruction.Create (OpCodes.Ldc_I4_7);
case 8:
return Instruction.Create (OpCodes.Ldc_I4_8);
}
}

if (intValue < 128)
return Instruction.Create (OpCodes.Ldc_I4_S, (sbyte)intValue);

return Instruction.Create (OpCodes.Ldc_I4, intValue);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Mono.Cecil;
using Mono.Linker;
using Mono.Linker.Steps;
using System;
using System.Linq;
using Xamarin.Android.Tasks;
using System.Collections.Generic;
using Mono.Cecil.Cil;

namespace MonoDroid.Tuner
{
public class GetAssembliesStep : BaseStep
{
AndroidLinkConfiguration config = null;

protected override void Process ()
{
config = AndroidLinkConfiguration.GetInstance (Context);
}

protected override void ProcessAssembly (AssemblyDefinition assembly)
{
if (config == null)
return;
config.Assemblies.Add (assembly);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ static Pipeline CreatePipeline (LinkerOptions options)
pipeline.AppendStep (new LoadI18nAssemblies (options.I18nAssemblies));

pipeline.AppendStep (new BlacklistStep ());

foreach (var desc in options.LinkDescriptions)
pipeline.AppendStep (new ResolveFromXmlStep (new XPathDocument (desc)));

Expand Down Expand Up @@ -121,6 +121,11 @@ static Pipeline CreatePipeline (LinkerOptions options)
pipeline.AppendStep (new StripEmbeddedLibraries ());
if (options.AddKeepAlives)
pipeline.AppendStep (new AddKeepAlivesStep (cache));

if (options.LinkResources) {
pipeline.AppendStep (new GetAssembliesStep ());
pipeline.AppendStep (new RemoveResourceDesignerStep ());
}
// end monodroid specific
pipeline.AppendStep (new RegenerateGuidStep ());
pipeline.AppendStep (new OutputStepWithTimestamps ());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ class LinkerOptions
public bool AddKeepAlives { get; set; }
public bool PreserveJniMarshalMethods { get; set; }
public bool DeterministicOutput { get; set; }
public bool LinkResources { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ void UpdateMagicPrefill (TypeDefinition magicType)
var instructions = methodPrefill.Body.Instructions;
instructions.Clear ();

instructions.Add (CreateLoadArraySizeOrOffsetInstruction (marshalTypes.Count));
instructions.Add (Extensions.CreateLoadArraySizeOrOffsetInstruction (marshalTypes.Count));
instructions.Add (Instruction.Create (OpCodes.Newobj, magicType.Module.ImportReference (genericMethodDictionaryCtor)));
instructions.Add (Instruction.Create (OpCodes.Stsfld, fieldTypesMap));

Expand All @@ -197,7 +197,7 @@ void UpdateMagicPrefill (TypeDefinition magicType)
foreach (var type in marshalTypes) {
instructions.Add (Instruction.Create (OpCodes.Ldsfld, fieldTypesMap));
instructions.Add (Instruction.Create (OpCodes.Ldstr, type.FullName.Replace ("/__<$>_jni_marshal_methods", "").Replace ("/","+")));
instructions.Add (CreateLoadArraySizeOrOffsetInstruction (idx++));
instructions.Add (Extensions.CreateLoadArraySizeOrOffsetInstruction (idx++));
instructions.Add (Instruction.Create (OpCodes.Callvirt, importedMethodSetItem));
}

Expand Down Expand Up @@ -265,40 +265,6 @@ static bool IsLdcI4 (Instruction instruction, out int intValue)
return true;
}

static Instruction CreateLoadArraySizeOrOffsetInstruction (int intValue)
{
if (intValue < 0)
throw new ArgumentException ($"{nameof (intValue)} cannot be negative");

if (intValue < 9) {
switch (intValue) {
case 0:
return Instruction.Create (OpCodes.Ldc_I4_0);
case 1:
return Instruction.Create (OpCodes.Ldc_I4_1);
case 2:
return Instruction.Create (OpCodes.Ldc_I4_2);
case 3:
return Instruction.Create (OpCodes.Ldc_I4_3);
case 4:
return Instruction.Create (OpCodes.Ldc_I4_4);
case 5:
return Instruction.Create (OpCodes.Ldc_I4_5);
case 6:
return Instruction.Create (OpCodes.Ldc_I4_6);
case 7:
return Instruction.Create (OpCodes.Ldc_I4_7);
case 8:
return Instruction.Create (OpCodes.Ldc_I4_8);
}
}

if (intValue < 128)
return Instruction.Create (OpCodes.Ldc_I4_S, (sbyte)intValue);

return Instruction.Create (OpCodes.Ldc_I4, intValue);
}

bool UpdateMarshalRegisterMethod (MethodDefinition method, HashSet<string> markedMethods)
{
var instructions = method.Body.Instructions;
Expand All @@ -310,7 +276,7 @@ bool UpdateMarshalRegisterMethod (MethodDefinition method, HashSet<string> marke
if (!arraySizeUpdated && idx + 1 < instructions.Count) {
int length;
if (IsLdcI4 (instructions [idx++], out length) && instructions [idx].OpCode == OpCodes.Newarr) {
instructions [idx - 1] = CreateLoadArraySizeOrOffsetInstruction (markedMethods.Count);
instructions [idx - 1] = Extensions.CreateLoadArraySizeOrOffsetInstruction (markedMethods.Count);
idx++;
arraySizeUpdated = true;
continue;
Expand Down Expand Up @@ -351,7 +317,7 @@ bool UpdateMarshalRegisterMethod (MethodDefinition method, HashSet<string> marke
continue;

if (markedMethods.Contains (mr.Name)) {
instructions [offsetIdx] = CreateLoadArraySizeOrOffsetInstruction (arrayOffset++);
instructions [offsetIdx] = Extensions.CreateLoadArraySizeOrOffsetInstruction (arrayOffset++);
continue;
}

Expand Down Expand Up @@ -436,7 +402,7 @@ protected override void DoAdditionalTypeProcessing (TypeDefinition type)
foreach (MethodReference method in type.Methods)
MarkMethod (method);
}

private void PreserveRegisteredMethod (TypeDefinition type, string member)
{
var type_ptr = type;
Expand Down
Loading