From ac01b522f002e3106f6c1c3b57c34e830c1061ea Mon Sep 17 00:00:00 2001 From: Sebastien Pouliot Date: Tue, 31 Mar 2020 17:20:44 -0400 Subject: [PATCH] [generator] Add missing `EnsureUIThread` for generated `init` and `initWithCoder` (#8250) Fix https://github.com/xamarin/xamarin-macios/issues/8249 --- src/generator.cs | 20 +++++++++--- tests/cecil-tests/Helper.cs | 7 +++-- tests/cecil-tests/Test.cs | 38 +++++++++++++++++++++++ tests/introspection/Mac/MacApiTypoTest.cs | 3 +- 4 files changed, 61 insertions(+), 7 deletions(-) diff --git a/src/generator.cs b/src/generator.cs index aa9eac083f..c32b318825 100644 --- a/src/generator.cs +++ b/src/generator.cs @@ -4184,20 +4184,25 @@ public partial class Generator : IMemberGatherer { string CurrentMethod; - void GenerateThreadCheck () + void GenerateThreadCheck (StreamWriter sw = null) { + string s; switch (CurrentPlatform) { case PlatformName.iOS: case PlatformName.WatchOS: case PlatformName.TvOS: - print ("global::{0}.UIApplication.EnsureUIThread ();", ns.Get ("UIKit")); + s = $"global::{ns.Get ("UIKit")}.UIApplication.EnsureUIThread ();"; break; case PlatformName.MacOSX: - print ("global::{0}.NSApplication.EnsureUIThread ();", ns.Get ("AppKit")); + s = $"global::{ns.Get ("AppKit")}.NSApplication.EnsureUIThread ();"; break; default: throw new BindingException (1047, CurrentPlatform); } + if (sw == null) + print (s); + else + sw.WriteLine (s); } // Stret.NeedStret is shared between generator and X.I dll so in order to wrap the exception @@ -6451,6 +6456,10 @@ public partial class Generator : IMemberGatherer { sw.WriteLine ("\tthrow new PlatformNotSupportedException (\"This API is not supported on this version of iOS\");"); sw.WriteLine ("\t\t#else"); } + if (type_needs_thread_checks) { + sw.Write ("\t\t\t"); + GenerateThreadCheck (sw); + } var indentation = 3; WriteIsDirectBindingCondition (sw, ref indentation, is_direct_binding, is_direct_binding_value, () => string.Format ("InitializeHandle (global::{1}.IntPtr_objc_msgSend (this.Handle, global::{2}.{0}), \"init\");", initSelector, ns.Messaging, ns.CoreObjCRuntime), @@ -6478,7 +6487,10 @@ public partial class Generator : IMemberGatherer { if (nscoding) { if (debug) sw.WriteLine ("\t\t\tConsole.WriteLine (\"{0}.ctor (NSCoder)\");", TypeName); - sw.WriteLine (); + if (type_needs_thread_checks) { + sw.Write ("\t\t\t"); + GenerateThreadCheck (sw); + } var indentation = 3; WriteIsDirectBindingCondition (sw, ref indentation, is_direct_binding, is_direct_binding_value, () => string.Format ("InitializeHandle (global::{1}.IntPtr_objc_msgSend_IntPtr (this.Handle, {0}, coder.Handle), \"initWithCoder:\");", initWithCoderSelector, ns.Messaging), diff --git a/tests/cecil-tests/Helper.cs b/tests/cecil-tests/Helper.cs index ac753f6f2d..833ad4de8d 100644 --- a/tests/cecil-tests/Helper.cs +++ b/tests/cecil-tests/Helper.cs @@ -63,8 +63,11 @@ namespace Cecil.Tests { yield return new TestCaseData (Path.Combine (Configuration.MonoTouchRootDirectory, "lib", "32bits", "Xamarin.iOS.dll")); yield return new TestCaseData (Path.Combine (Configuration.MonoTouchRootDirectory, "lib", "64bits", "Xamarin.iOS.dll")); - yield return new TestCaseData (Configuration.XamarinWatchOSDll); - yield return new TestCaseData (Configuration.XamarinTVOSDll); + // XamarinWatchOSDll is stripped of its IL + yield return new TestCaseData (Path.Combine (Configuration.MonoTouchRootDirectory, "lib", "32bits", "Xamarin.WatchOS.dll")); + // XamarinTVOSDll is stripped of it's IL + yield return new TestCaseData (Path.Combine (Configuration.MonoTouchRootDirectory, "lib", "64bits", "Xamarin.TVOS.dll")); + yield return new TestCaseData (Configuration.XamarinMacMobileDll); yield return new TestCaseData (Configuration.XamarinMacFullDll); } diff --git a/tests/cecil-tests/Test.cs b/tests/cecil-tests/Test.cs index c7eadb320f..edc20ea605 100644 --- a/tests/cecil-tests/Test.cs +++ b/tests/cecil-tests/Test.cs @@ -48,5 +48,43 @@ namespace Cecil.Tests { } } } + + [TestCaseSource (typeof (Helper), "PlatformAssemblies")] + // ref: https://github.com/xamarin/xamarin-macios/issues/8249 + public void EnsureUIThreadOnInit (string assemblyPath) + { + var assembly = Helper.GetAssembly (assemblyPath); + if (assembly == null) { + Assert.Ignore ("{assemblyPath} could not be found (might be disabled in build)"); + return; // just to help nullability + } + + // `CNContactsUserDefaults` is `[ThreadSafe (false)]` and part of iOS and macOS + var t = assembly.MainModule.GetType ("Contacts.CNContactsUserDefaults"); + if (t == null) { + // tvOS does not have the type so let's find an alternative + t = assembly.MainModule.GetType ("PhotosUI.PHLivePhotoView"); + } + if (t == null) { + Assert.Fail ($"No type found for {assembly}"); + return; // just to help nullability + } + + foreach (var c in t.Methods) { + if (!c.IsConstructor || c.IsStatic || c.HasParameters) + continue; + // .ctor(IntPtr) + var found = false; + foreach (var ins in c.Body.Instructions) { + if (ins.OpCode.Code != Code.Call) + continue; + found |= (ins.Operand as MethodReference)?.Name == "EnsureUIThread"; + } + if (!found) + Assert.Fail ("EnsureUIThread missing"); + else + return; // single case, no point in iterating anymore + } + } } } diff --git a/tests/introspection/Mac/MacApiTypoTest.cs b/tests/introspection/Mac/MacApiTypoTest.cs index 9f310fb6ee..e8531ab404 100644 --- a/tests/introspection/Mac/MacApiTypoTest.cs +++ b/tests/introspection/Mac/MacApiTypoTest.cs @@ -12,7 +12,7 @@ namespace Introspection [TestFixture] public class MacApiTypoTest : ApiTypoTest { - NSSpellChecker checker = new NSSpellChecker (); + NSSpellChecker checker; [SetUp] public void SetUp () @@ -20,6 +20,7 @@ namespace Introspection var sdk = new Version (Constants.SdkVersion); if (!PlatformHelper.CheckSystemVersion (sdk.Major, sdk.Minor)) Assert.Ignore ("Typos only verified using the latest SDK"); + checker = new NSSpellChecker (); } public override string GetTypo (string txt)