From d481a15426c912f4db693b38efc9bfe7612445c2 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 28 Jun 2023 17:06:04 -0500 Subject: [PATCH] [WIP] marshal methods & IntPtr.Size Fixes: https://github.com/xamarin/xamarin-android/issues/8155 --- .../Tasks/LinkerTests.cs | 51 ++++++++++++++++++- .../MarshalMethodsAssemblyRewriter.cs | 42 ++++++--------- 2 files changed, 66 insertions(+), 27 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs index 823c2deec46..c1e7f610f4e 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs @@ -1,9 +1,12 @@ using System; using System.IO; using System.Linq; +using System.Text; using Java.Interop.Tools.Cecil; using Mono.Cecil; +using Mono.Cecil.Cil; using Mono.Linker; +using Mono.Tuner; using MonoDroid.Tuner; using NUnit.Framework; using Xamarin.ProjectTools; @@ -525,13 +528,24 @@ public void DoNotErrorOnPerArchJavaTypeDuplicates () lib.Sources.Add (new BuildItem.Source ("Library1.cs") { TextContent = () => @" namespace Lib1; -public class Library1 : Java.Lang.Object { +public class Library1 : Com.Example.Androidlib.MyRunner { private static bool Is64Bits = IntPtr.Size >= 8; public static bool Is64 () { return Is64Bits; } + + public override void Run () => Console.WriteLine (Is64Bits); }", + }); + lib.Sources.Add (new BuildItem ("AndroidJavaSource", "MyRunner.java") { + Encoding = new UTF8Encoding (encoderShouldEmitUTF8Identifier: false), + TextContent = () => @" +package com.example.androidlib; + +public abstract class MyRunner { + public abstract void run(); +}" }); var proj = new XamarinAndroidApplicationProject { IsRelease = true, ProjectName = "App1" }; proj.References.Add(new BuildItem.ProjectReference (Path.Combine ("..", "Lib1", "Lib1.csproj"), "Lib1")); @@ -545,6 +559,41 @@ public static bool Is64 () { using var b = CreateApkBuilder (Path.Combine (path, "App1")); Assert.IsTrue (lb.Build (lib), "build should have succeeded."); Assert.IsTrue (b.Build (proj), "build should have succeeded."); + + var intermediate = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath); + var dll = $"{lib.ProjectName}.dll"; + Assert64Bit ("android-arm", expected64: false); + Assert64Bit ("android-arm64", expected64: true); + Assert64Bit ("android-x86", expected64: false); + Assert64Bit ("android-x64", expected64: true); + + void Assert64Bit(string rid, bool expected64) + { + var assembly = AssemblyDefinition.ReadAssembly (Path.Combine (intermediate, rid, "linked", "shrunk", dll)); + var type = assembly.MainModule.FindType ("Lib1.Library1"); + Assert.NotNull (type, "Should find Lib1.Library1!"); + var cctor = type.GetTypeConstructor (); + Assert.NotNull (type, "Should find Lib1.Library1.cctor!"); + Assert.AreNotEqual (0, cctor.Body.Instructions.Count); + + /* + * IL snippet + * .method private hidebysig specialname rtspecialname static + * void .cctor () cil managed + * { + * // Is64Bits = 4 >= 8; + * IL_0000: ldc.i4 4 + * IL_0005: ldc.i4.8 + * ... + */ + var instruction = cctor.Body.Instructions [0]; + Assert.AreEqual (OpCodes.Ldc_I4, instruction.OpCode); + if (expected64) { + Assert.AreEqual (8, instruction.Operand, $"Expected 64-bit: {expected64}"); + } else { + Assert.AreEqual (4, instruction.Operand, $"Expected 64-bit: {expected64}"); + } + } } } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs index 4131349d150..bd3be6ce978 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs @@ -114,47 +114,37 @@ public void Rewrite (DirectoryAssemblyResolver resolver, List targetAsse } } - var newAssemblyPaths = new List (); + var newAssemblyPaths = new List> (); foreach (AssemblyDefinition asm in uniqueAssemblies) { - foreach (string path in GetAssemblyPaths (asm)) { + foreach (string original in GetAssemblyPaths (asm)) { var writerParams = new WriterParameters { - WriteSymbols = File.Exists (Path.ChangeExtension (path, ".pdb")), + WriteSymbols = File.Exists (Path.ChangeExtension (original, ".pdb")), }; - string directory = Path.Combine (Path.GetDirectoryName (path), "new"); + string directory = Path.Combine (Path.GetDirectoryName (original), "new"); Directory.CreateDirectory (directory); - string output = Path.Combine (directory, Path.GetFileName (path)); - log.LogDebugMessage ($"Writing new version of assembly: {output}"); + string temp = Path.Combine (directory, Path.GetFileName (original)); + log.LogDebugMessage ($"Writing new version of assembly: {temp}"); // TODO: this should be used eventually, but it requires that all the types are reloaded from the assemblies before typemaps are generated // since Cecil doesn't update the MVID in the already loaded types //asm.MainModule.Mvid = Guid.NewGuid (); - asm.Write (output, writerParams); - newAssemblyPaths.Add (output); + asm.Write (temp, writerParams); + newAssemblyPaths.Add ((original, temp)); } } // Replace old versions of the assemblies only after we've finished rewriting without issues, otherwise leave the new // versions around. - foreach (string path in newAssemblyPaths) { - string? pdb = null; - - string source = Path.ChangeExtension (path, ".pdb"); - if (File.Exists (source)) { - pdb = source; - } - - foreach (string targetPath in targetAssemblyPaths) { - string target = Path.Combine (targetPath, Path.GetFileName (path)); - CopyFile (path, target); - - if (!String.IsNullOrEmpty (pdb)) { - CopyFile (pdb, Path.ChangeExtension (target, ".pdb")); - } + foreach ((string original, string temp) in newAssemblyPaths) { + CopyFile (temp, original); + RemoveFile (temp); + + var pdb = Path.ChangeExtension (temp, ".pdb"); + if (File.Exists(pdb)) { + CopyFile (pdb, Path.ChangeExtension (original, ".pdb")); + RemoveFile (pdb); } - - RemoveFile (path); - RemoveFile (pdb); } void CopyFile (string source, string target)