[dotnet-linker] Handle null fields in BackingFieldDelayHandler as unmarked fields. Fixes #16957. (#16970)

The BackingFieldDelayHandler will temporarily remove the body of Dispose
methods, and then for every field accessed in the Dispose method that was
preserved by the linker, we'll keep the corresponding code in the Dispose
method (otherwise we'd remove the code).

This is a way to remove fields that are _only_ accessed (and nulled out) in
the Dispose method.

However, we were running into a problem with determining if a field was marked
by the linker: if the field is in a generic type, and that field was not
marked by the linker, the linker might have actually removed the field from
the containing type before we're processing the Dispose methods, and we'd find
a null field definition where no null field definition was expected
(eventually resulting in an ArgumentNullException).

Fix this by treating a null field definition as an unmarked field.

Also add a test.

Fixes https://github.com/xamarin/xamarin-macios/issues/16957.
This commit is contained in:
Rolf Bjarne Kvinge 2022-12-07 15:53:15 +01:00 коммит произвёл GitHub
Родитель 6581fcb5fb
Коммит 25fc6c84d1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 18 добавлений и 2 удалений

Просмотреть файл

@ -52,5 +52,19 @@ namespace LinkAnyTest {
Assert.IsNotNull (AppContext.GetData ("PINVOKE_OVERRIDE"), "PINVOKE_OVERRIDE");
}
#endif
#if !__WATCHOS__
[Test]
public void BackingFieldInGenericType ()
{
// https://github.com/dotnet/linker/issues/3148
#if __MACOS__
var view = new AppKit.NSView ();
#else
var view = new UIKit.UIView ();
#endif
GC.KeepAlive (view.HeightAnchor);
}
#endif // !__WATCHOS__
}
}

Просмотреть файл

@ -70,8 +70,10 @@ namespace Xamarin.Linker {
foreach (var ins in body.Instructions) {
switch (ins.OpCode.OperandType) {
case OperandType.InlineField:
var field = (ins.Operand as FieldReference)?.Resolve ();
if (!context.Annotations.IsMarked (field)) {
var fr = ins.Operand as FieldReference;
var field = fr?.Resolve ();
var isMarked = field is not null && context.Annotations.IsMarked (field);
if (!isMarked) {
var store_field = ins;
var load_null = ins.Previous;
var load_this = ins.Previous.Previous;