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
2 changes: 1 addition & 1 deletion tests/monotouch-test/AppKit/DerivedEventTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ void TestDelegates (NSComboBox b)
public void DerivedEvents_OverwriteThrows ()
{
#if RELEASE
var checkTrimmedAway = TestRuntime.IsLinkAll;
var checkTrimmedAway = TestRuntime.IsLinkAny;
#else
var checkTrimmedAway = false;
#endif
Expand Down
2 changes: 1 addition & 1 deletion tests/monotouch-test/AppKit/NSView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public void AllItemsWithNSMenuShouldAllowNull ()
foreach (var ctor in types) {
var o = ctor ();
var prop = o.GetType ().GetProperty ("Menu", BindingFlags.Public | BindingFlags.Instance);
if (prop is null && TestRuntime.IsLinkAll)
if (prop is null && TestRuntime.IsLinkAny)
continue; // the property was linked away.
prop.SetValue (o, null, null);
}
Expand Down
4 changes: 2 additions & 2 deletions tests/monotouch-test/ObjCRuntime/EveryFrameworkSmokeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ LoadStatus CheckLoadFailure (string libraryName, string path)
[Test]
public void ExpectedLibrariesAreLoaded ()
{
if (TestRuntime.IsLinkAll)
Assert.Ignore ("This test will fail when all assemblies are linked, since we won't link with all frameworks in that case.");
if (TestRuntime.IsLinkAny)
Assert.Ignore ("This test will fail when assemblies are linked, since we won't link with all frameworks in that case.");

List<string> failures = new List<string> ();

Expand Down
4 changes: 4 additions & 0 deletions tests/xharness/Jenkins/TestVariationsFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ IEnumerable<TestData> GetTestData (RunTestTask test)
yield return new TestData { Variation = "Debug (don't bundle original resources)", TestVariation = "do-not-bundle-original-resources" };
}
break;
case "monotouch-test":
yield return new TestData { Variation = "Release (link sdk)", TestVariation = "release|linksdk", Ignored = ignore };
yield return new TestData { Variation = "Release (link all)", TestVariation = "release|linkall", Ignored = ignore };
break;
}

switch (test.ProjectPlatform) {
Expand Down
80 changes: 75 additions & 5 deletions tools/dotnet-linker/Steps/PreserveBlockCodeStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,16 @@ protected override bool IsActiveFor (AssemblyDefinition assembly)

protected override bool ProcessType (TypeDefinition type)
{
if (!GetMembersToPreserve (type, out var field, out var method))
return false;

var modified = false;
modified |= abr.AddDynamicDependencyAttributeToStaticConstructor (type, field);
modified |= abr.AddDynamicDependencyAttributeToStaticConstructor (type, method);

if (GetMembersToPreserve (type, out var field, out var method)) {
modified |= abr.AddDynamicDependencyAttributeToStaticConstructor (type, field);
modified |= abr.AddDynamicDependencyAttributeToStaticConstructor (type, method);
}

if (GetNewStyleMethodToPreserve (type, out var invokeMethod))
modified |= abr.AddDynamicDependencyAttributeToStaticConstructor (type, invokeMethod);

return modified;
}

Expand Down Expand Up @@ -103,5 +107,71 @@ static internal void Invoke (IntPtr block, int magic_number)
// The type was used, so preserve the method and field
return true;
}

// New-style block proxy types use [UnmanagedCallersOnly] on the Invoke method
// and don't have a Handler field. They are generated when the bgen tool emits
// function pointer-based block trampolines. We need to preserve the Invoke method
// because the runtime looks it up via reflection in Blocks.GetBlockForDelegate.
public static bool GetNewStyleMethodToPreserve (TypeDefinition type, [NotNullWhen (true)] out MethodDefinition? method)
{
method = null;

/* For the following class:

static internal class SDRegistrarTestBlock {
[UnmanagedCallersOnly]
[UserDelegateType (typeof (RegistrarTestBlock))]
internal static unsafe uint Invoke (IntPtr block, uint magic)
{
}
internal static unsafe BlockLiteral CreateBlock (RegistrarTestBlock callback)
{
delegate* unmanaged<IntPtr, uint, uint> trampoline = &Invoke;
return new BlockLiteral (trampoline, callback, typeof (SDRegistrarTestBlock), nameof (Invoke));
}
}

* We need to make sure the linker doesn't remove the Invoke method.
*/

// The type must be abstract, sealed (static class) and nested
if (!type.IsAbstract || !type.IsSealed || !type.IsNested)
return false;

// The type must not have fields (old-style types have a Handler field and are handled by GetMembersToPreserve)
if (type.HasFields)
return false;

// The type is nested inside ObjCRuntime.Trampolines class
var nestingType = type.DeclaringType;
if (!nestingType.Is ("ObjCRuntime", "Trampolines"))
return false;

if (!type.HasMethods)
return false;

// The class has an 'Invoke' method with [UnmanagedCallersOnly] and [UserDelegateType] attributes
method = type.Methods.SingleOrDefault (v => {
if (v.Name != "Invoke")
return false;
if (!v.HasParameters)
return false;
if (!v.HasCustomAttributes)
return false;
var hasUnmanagedCallersOnly = false;
var hasUserDelegateType = false;
foreach (var attr in v.CustomAttributes) {
if (attr.AttributeType.Name == "UnmanagedCallersOnlyAttribute")
hasUnmanagedCallersOnly = true;
else if (attr.AttributeType.Name == "UserDelegateTypeAttribute")
hasUserDelegateType = true;
if (hasUnmanagedCallersOnly && hasUserDelegateType)
break;
}
return hasUnmanagedCallersOnly && hasUserDelegateType;
});

return method is not null;
}
}
}
Loading