Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -514,7 +517,7 @@ public void AndroidUseNegotiateAuthentication ([Values (true, false, null)] bool
}

[Test]
public void DoNotErrorOnPerArchJavaTypeDuplicates ()
public void DoNotErrorOnPerArchJavaTypeDuplicates ([Values(true, false)] bool enableMarshalMethods)
{
if (!Builder.UseDotNet)
Assert.Ignore ("Test only valid on .NET");
Expand All @@ -525,26 +528,73 @@ 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"));
proj.MainActivity = proj.DefaultMainActivity.Replace (
"base.OnCreate (bundle);",
"base.OnCreate (bundle);\n" +
"if (Lib1.Library1.Is64 ()) Console.WriteLine (\"Hello World!\");");
proj.SetProperty ("AndroidEnableMarshalMethods", enableMarshalMethods.ToString ());


using var lb = CreateDllBuilder (Path.Combine (path, "Lib1"));
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}");
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,47 +114,37 @@ public void Rewrite (DirectoryAssemblyResolver resolver, List<string> targetAsse
}
}

var newAssemblyPaths = new List<string> ();
var newAssemblyPaths = new List<(string original, string temp)> ();
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)
Expand Down