* The 'Microsoft.NET.Workload.Emscripten.Current.Manifest-8.0.100.Transport' dependency was renamed to 'Microsoft.NET.Workload.Emscripten.Current.Manifest-8.0.100'
* Add a separate dependency for 'Microsoft.NET.ILLink', since it can have a different version than 'Microsoft.NET.ILLink.Tasks'.
* Update using the '.NET 8' and '.NET 8.0.1xx' channels.
Fixed an issue where the C# class name was coming in with `,
AssemblyName` tacked on the end.
Fixed an issue where a class that had an entry in the map didn't have a
`class_ptr` which was causing an NRE.
Fixed a predicate for deciding when to mark the assembly for save.
Note - for an as yet undetermined reason, the linker is not picking up
that I'm marking the assembly for save, which is why the `true` test
cases are commented out.
This fixes issue 16671 https://github.com/xamarin/xamarin-macios/issues/16671
There is an issue with managed static registrar and explicitly rooted
assemblies (through linker descriptor files) where the linker does not
emit the created module initializer.
This was originally reported in:
https://github.com/dotnet/runtime/issues/92106
and a smaller repro can be set up in: `tests/dotnet/MySingleView` test
project with the following changes:
### Smaller repro
1. Add a descriptor xml - `Roots.xml`
```xml
<linker>
<assembly fullname="MySingleView" preserve="All" />
</linker>
```
2. Include the root descriptor in the `MySingleView.csproj`
```xml
<ItemGroup>
<TrimmerRootDescriptor Condition="'$(_RootSelf)' == 'true'" Include="Roots.xml" />
</ItemGroup>
```
3. Publish/run the app on a device
```bash
dotnet publish -c Release -r ios-arm64 -p:PublishAot=true -p:PublishAotUsingRuntimePack=true -p:_RootSelf=true -t:Run /bl
```
4. The app crashes at start up with:
```
2023-09-25 18:55:42.562 MySingleView[477:55844] *** Terminating app due to uncaught exception 'ObjCRuntime.RuntimeException', reason: 'Can't register the class MySingleView.AppDelegate when the dynamic registrar has been linked away. (ObjCRuntime.RuntimeException)
at ObjCRuntime.Class.GetClassHandle(Type, Boolean, Boolean&) + 0x1c4
at UIKit.UIApplication.Main(String[], Type, Type) + 0x8c
at MySingleView!<BaseAddress>+0x2fae7c
'
```
NOTE: This is reported for NativeAOT but seems like a general problem
Additionally, here are the disassembled linked assemblies:
- regular:
https://gist.github.com/ivanpovazan/e82bdcb5313a846ff50de0a79d560553
- rooted:
https://gist.github.com/ivanpovazan/760e931fdcd3ef3e573d88087718dad2
It can be seen that the rooted one is missing:
```asm
// ================== GLOBAL METHODS =========================
.method private hidebysig specialname rtspecialname static
void .cctor() cil managed
{
// Code size 11 (0xb)
.maxstack 1
IL_0000: newobj instance void ObjCRuntime.__Registrar__::.ctor()
IL_0005: call void [Microsoft.iOS]ObjCRuntime.RegistrarHelper::Register(class [Microsoft.iOS]ObjCRuntime.IManagedRegistrar)
IL_000a: ret
} // end of global method .cctor
```
### The fix
This PR fixes this behaviour by explicitly marking module constructor to
prevent this from happening.
---
Fixes https://github.com/dotnet/runtime/issues/92106
This PR implements a workaround for the following build warning:
```
ILC: Method '[Microsoft.iOS]Foundation.NSObject.RegisterToggleRef(NSObject,native int,bool)' will always throw because:
Invalid IL or CLR metadata in 'Void Foundation.NSObject.RegisterToggleRef(Foundation.NSObject, IntPtr, Boolean)'
```
Ref #18524
`None` seems to be equivalent to not having the attribute at all. The type will be stripped by ILLink and ILC. The `PublicConstructors | NonPublicConstructors` seems to be a better option to keep the type while making it possible to trim as much of its members as possible.
This change resolves two warnings:
```
ILLink : warning IL2037: <Module>..cctor(): No members were resolved for 'None' on type 'Microsoft.Maui.Controls.Handlers.Compatibility.FrameRenderer.FrameView'.
ILC : warning IL2037: <Module>..cctor(): No members were resolved for '0' on type 'Microsoft.Maui.Controls.Handlers.Compatibility.FrameRenderer.FrameView'.
```
(Note: these warnings will still be present for _static_ classes which don't have a static constructor)
@rolfbjarne I believe we don't need https://github.com/dotnet/runtime/pull/90154 anymore
---------
Co-authored-by: Simon Rozsival <simon@rozsival.com>
This is a follow-up to #18854
The code that we use to generate method signatures still isn't working
properly. Method signatures that contain constructed generic types are
incorrect (this is a problem for example for UIGestureRecognizer
subclasses in MAUI). We are also handling multidimensional arrays
incorrectly.
Instead of implementing the signature builder ourselves, we can utilize
the `DocCommentId.GetDocCommentId (member)` method from
Mono.Cecil.Rocks.
```c#
void Method(IEnumerable<int> arg) { }
// before
[DynamicDependency ("Method(System.Collections.Generic.IEnumerable`1<System.Int32>)", typeof (MyClass))]
// after
[DynamicDependency ("Method(System.Collections.Generic.IEnumerable{System.Int32})", typeof (MyClass))]
```
We're going to change the pack names to support multi-targeting, so ahead
of the pack name change I'm changing the existing logic to use a variable
for the pack name in most places (this will make the rename much easier and
simpler).
These changes should have no effect by themselves.
I noticed that we're generating invalid DynamicDependency attributes for generic types and methods. For example for the `void Activated(T sender)` method of `UIKit.UIGestureRecognizer.Callback<T>`:
```c#
// before:
[DynamicDependency("Activated(T)", typeof(UIKit.UIGestureRecognizer.Callback<>))]
// after:
[DynamicDependency("Activated(`0)", "UIKit.UIGestureRecognizer.Callback`1", "Microsoft.MacCatalyst")]
```
---------
Co-authored-by: Simon Rozsival <simon@rozsival.com>
This is a follow-up to #18666
This PR resolves the following warnings that I've seen in build logs
when running MySingleView with NativeAOT:
```
ILLink : warning IL2034: <Module>..cctor(): The 'DynamicDependencyAttribute' could not be analyzed. [/..../xamarin/xamarin-macios/tests/dotnet/MySingleView/MySingleView.csproj]
ILLink : warning IL2034: <Module>..cctor(): The 'DynamicDependencyAttribute' could not be analyzed. [/..../xamarin/xamarin-macios/tests/dotnet/MySingleView/MySingleView.csproj]
...
```
The generated module cctor code looks like this:
```c#
internal class <Module>
{
[DynamicDependency("InvokeConformsToProtocol(ObjCRuntime.NativeHandle)", typeof(NSObject))]
[DynamicDependency("Foundation.NSObject", null)]
[DynamicDependency("ConformsToProtocol(ObjCRuntime.NativeHandle)", typeof(NSObject))]
[DynamicDependency("Foundation.NSObject", null)]
// ...
static <Module>()
{
// ...
}
}
```
The `[DynamicDependency("T", null)]` attributes are invalid. We could
change them to `[DynamicDependency(DynamicallyAccessedMemberTypes.None,
typeof(T))]` but I think that attribute is redundant because the type
itself is always already preserved through the attribute that precedes
it with a selector of one of its methods/fields.
This is a follow-up to #18666
The idea is to transform `[Preserve(Conditional = true)]` into
`[DynamicDependency(signature, type)]` on the static constructor of the
given type:
```c#
class MyClass {
[Preserve (Conditional = true)]
public void MyMethod () { }
// transformed into ...
[DynamicDependency ("MyMethod", typeof (MyClass))]
static MyClass() { /* either the existing cctor or a new empty cctor */ }
public void MyMethod () { }
}
```
Add partial support for the `[Preserve]` attribute for NativeAOT. This
is done by injecting an equivalent `[DynamicDependency]` attribute. The
partial support comes from the fact that there's no way to map a
conditional preserve attribute (`[Preserve (Conditional = true)]`) to a
`[DynamicDependency]` attribute, so we report a warning instead.
For non-conditional `[Preserve]` attributes, we'll now add a
`[DynamicDependency]` attribute to the assembly's module constructor for
the type/member in question, effectively rooting it.
This makes it possible to fully link all our test suites when NativeAOT
(otherwise NativeAOT would just link out all the tests, leaving the test
suites empty - and unfortunately green, so this was a rather accidental
discovery).
This is a bit non-obvious, but we later use the presence of such a
stored list to determine if we need to generate the __Registrar__ type in the
assembly.
The problem is that we need to generate the __Registrar__ type even if
no trampolines were created, because we use it for numerous other purposes
as well (type lookup for instance).
Fixes https://github.com/xamarin/xamarin-macios/issues/18570.
This PR adds lookup tables and factory methods for INativeObjects and
NSObjects. These generated methods allow us to create instances of these
objects without needing reflection.
Closes#18358.
We can determine the exact constructor to call when creating
INativeObject instances in the generated code, so do exactly that.
This avoids a lot of slow reflection, and also makes the constructor
call visible for any linkers.
This also fixes a compiler warnings:
> tools/common/Rewriter.cs(333,25): warning CS8604: Possible null
reference argument for parameter 'path1' in 'string Path.Combine(string
path1, string path2)'.
And make nullability warnings in dotnet-linker show up as errors to
prevent future introduction of nullability warnings.
Closes#18357
This PR implements the idea described in the linked issue:
Whenever there's an NSObject constructor that we call from a registrar
callback, we need to create
a separate constructor that will first set the `handle` and `flags`
values of the NSObject before
calling the original constructor. Here's an example of the code we
generate:
```csharp
// The original constructor:
public .ctor (T0 p0, T1 p1, ...) { /* ... */ }
// The generated constructor with pre-initialization:
public .ctor (T0 p0, T1 p1, ..., IntPtr handle, IManagedRegistrar dummy) {
this.handle = (NativeHandle)handle;
this.flags = 2; // Flags.NativeRef == 2
this..ctor (p0, p1, ...);
}
```
- This code can't be expressed in C# and it can only be expressed
directly in IL.
- The reason we need to do this is because the base NSObject
parameterless constructor
would allocate a new Objective-C object if `handle` is a zero pointer.
- The `IManagedRegistrar` type is added only to make the constructor
signature unique (IManagedRegistrar is not a public class and customers
can't use it in their apps directly)
This PR disables passing `-dead_strip` to the native linker in case of
NativeAOT runtime to prevent build failures.
Additionally, this change affects the size of the application in the
following way - measured with `dotnet new maui` app version
`8.0.0-preview.7.23359.1`:
| MAUI iOS | -dead_strip | no -dead_strip | diff (b) | diff (Kb) | diff
(%) |
|----------|--------------|----------|----------|-----------|----------|
| .ipa (b) | 13377583 | 13435276 | 57693 | 57,693 | 0,43% |
| Size on disk (b) | 41883897 | 42038873 | 154976 | 154,976 | 0,37% |
| binary (b) | 39614336 | 39769312 | 154976 | 154,976 | 0,39% |
Even though the size of the application regresses, with this change we
have a more stable product.
Finally, once this PR gets merged we can open a tracking issue to solve
the size regression either by fixing:
- https://github.com/dotnet/runtime/issues/88032 or
- by manually removing the dead code as proposed by @filipnavara here:
https://github.com/dotnet/runtime/issues/88032#issuecomment-1624303167
---
Fixes: https://github.com/xamarin/xamarin-macios/issues/18552
Closes#18356
In the static UnmanagedCallersOnly methods we don't know the generic
parameters of the type we're working with and we need to use this trick
to be able to call methods on the generic type without using reflection.
When we call a non-generic interface method implemented on a generic
class, the .NET runtime will resolve the generic parameters for us. In
the implementation of the interface method, we can simply use the
generic parameters and generate the same code we usually generate in the
UnmanagedCallersOnly callback method.
This is an example of the code we generate in addition to user code:
```csharp
internal interface __IRegistrarGenericTypeProxy__CustomNSObject_1__
{
void __IRegistrarGenericTypeProxy__CustomNSObject_1____SomeMethod (IntPtr p0);
}
public class CustomNSObject<T> : NSObject, __IRegistrarGenericTypeProxy__CustomNSObject_1__
where T : NSObject
{
[Export ("someMethod:")]
public void SomeMethod (T someInput)
{
// ...
}
// generated implementation of the proxy interface:
public void __IRegistrarGenericTypeProxy__CustomNSObject_1____SomeMethod (IntPtr sel, IntPtr p0, IntPtr* exception_gchandle)
{
try {
var obj0 = (T) Runtime.GetNSObject<T> (p0);
SomeMethod (obj0);
} catch (Exception ex) {
*exception_gchandle = Runtime.AllocGCHandle (ex);
}
}
// generated registrar callbacks:
private static class __Registrar_Callbacks__
{
[UnmanagedCallersOnly (EntryPoint = "_callback_1_CustomNSObject_1_SomeMethod")]
public unsafe static void callback_1_CustomNSObject_1_SomeMethod (IntPtr pobj, IntPtr sel, IntPtr p0, IntPtr* exception_gchandle)
{
var proxy = (__IRegistrarGenericTypeProxy__CustomNSObject_1__)Runtime.GetNSObject (pobj);
proxy.__IRegistrarGenericTypeProxy__CustomNSObject_1____SomeMethod (sel, p0, exception_gchandle);
}
}
}
```
```csharp
// regular non-generic class for comparison:
public class CustomNSObject : NSObject
where T : NSObject
{
[Export ("someMethod:")]
public void SomeMethod (NSSet someInput)
{
// ...
}
private static class __Registrar_Callbacks__
{
[UnmanagedCallersOnly (EntryPoint = "_callback_1_CustomNSObject_1_SomeMethod")]
public unsafe static void callback_1_CustomNSObject_1_SomeMethod (IntPtr pobj, IntPtr sel, IntPtr p0, IntPtr* exception_gchandle)
{
try {
NSSet obj0 = Runtime.GetNSObject<NSSet> (p0);
SomeMethod (obj0);
} catch (Exception ex) {
*exception_gchandle = Runtime.AllocGCHandle (ex);
}
}
}
}
```
---------
Co-authored-by: Alex Soto <alex@alexsoto.me>