Skip to content

Commit

Permalink
Merge pull request unoplatform#14324 from Youssef1313/native-ctors-ge…
Browse files Browse the repository at this point in the history
…n-fix-hr

fix(hr): Fix RegisterAttribute generation for nested/generic types
  • Loading branch information
MartinZikmund authored Nov 8, 2023
2 parents 2d9c2bd + a5362ab commit 78790e7
Show file tree
Hide file tree
Showing 12 changed files with 315 additions and 275 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Uno;
using Uno.Roslyn;
using Microsoft.CodeAnalysis.PooledObjects;
using System.Diagnostics;

namespace Microsoft.CodeAnalysis
{
Expand Down Expand Up @@ -398,7 +399,10 @@ ITypeSymbol ts when ts.ContainingType is ISymbol pt
return type?.GetFullyQualifiedTypeExcludingGlobal();
}

public static string GetFullMetadataName(this ITypeSymbol symbol)
// forRegisterAttributeDotReplacement is used specifically by NativeCtorsGenerator to generate for the Android/iOS RegisterAttribute
// A non-null value means we are generating for RegisterAttribute, and we replace invalid characters with '_'.
// The '.' is special cased to be replaced by the value of forRegisterAttributeDotReplacement, whether it's '_' or '/'
public static string GetFullMetadataName(this ITypeSymbol symbol, char? forRegisterAttributeDotReplacement = null)
{
ISymbol s = symbol;
var sb = new StringBuilder(s.MetadataName);
Expand Down Expand Up @@ -428,9 +432,19 @@ public static string GetFullMetadataName(this ITypeSymbol symbol)

var namedType = symbol as INamedTypeSymbol;

if (namedType?.TypeArguments.Any() ?? false)
// When generating for RegisterAttribute, the name we pass to the attribute is used by Xamarin tooling for generating Java files, specifically, it's used for the class name.
// The characters '.', '+', and '`' are not valid characters for a class name.
// On Android, we use '/' as replacement for '.' to match Jni name:
// https://github.com/xamarin/java.interop/blob/38c8a827e78ffe9c80ad2313a9e0e0d4f8215184/src/Java.Interop.Tools.TypeNameMappings/Java.Interop.Tools.TypeNameMappings/JavaNativeTypeManager.cs#L693-L699
if (forRegisterAttributeDotReplacement.HasValue)
{
var genericArgs = namedType.TypeArguments.Select(GetFullMetadataName).JoinBy(",");
var replacement = forRegisterAttributeDotReplacement.Value;
sb.Replace('.', replacement).Replace('+', '_').Replace('`', '_');
}
else if (namedType?.TypeArguments.Any() ?? false)
{
// We don't append type arguments when generating for RegisterAttribute because '[' and ']' are invalid characters for a class name.
var genericArgs = namedType.TypeArguments.Select(a => GetFullMetadataName(a, null)).JoinBy(",");
sb.Append($"[{genericArgs}]");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Uno.UI.SourceGenerators.Helpers;
using System.Diagnostics;
using Uno.Extensions;
using Uno.Roslyn;

Expand Down Expand Up @@ -37,7 +36,7 @@ private class SerializationMethodsGenerator : SymbolVisitor
private readonly INamedTypeSymbol? _androidViewSymbol;
private readonly INamedTypeSymbol? _intPtrSymbol;
private readonly INamedTypeSymbol? _jniHandleOwnershipSymbol;
private readonly INamedTypeSymbol?[]? _javaCtorParams;
private readonly INamedTypeSymbol?[] _javaCtorParams;
private readonly string _configuration;
private readonly bool _isDebug;
private readonly bool _isHotReloadEnabled;
Expand Down Expand Up @@ -129,7 +128,7 @@ private void ProcessType(INamedTypeSymbol typeSymbol)

if (isAndroidView)
{
Func<IMethodSymbol, bool> predicate = m => m.Parameters.Select(p => p.Type).SequenceEqual(_javaCtorParams ?? Array.Empty<ITypeSymbol?>());
Func<IMethodSymbol, bool> predicate = m => m.Parameters.Select(p => p.Type).SequenceEqual(_javaCtorParams);
var nativeCtor = GetNativeCtor(typeSymbol, predicate, considerAllBaseTypes: false);

if (nativeCtor == null && GetNativeCtor(typeSymbol.BaseType, predicate, considerAllBaseTypes: true) != null)
Expand Down Expand Up @@ -164,7 +163,7 @@ string GetGeneratedCode(INamedTypeSymbol typeSymbol)
// generated at runtime

var registerParamApple = _isHotReloadEnabled
? $"\"{typeSymbol.GetFullMetadataName().Replace(".", "_")}\""
? $"\"{typeSymbol.GetFullMetadataName(forRegisterAttributeDotReplacement: '_')}\""
: "";

builder.AppendLineIndented($"[global::Foundation.Register({registerParamApple})]");
Expand All @@ -175,7 +174,7 @@ string GetGeneratedCode(INamedTypeSymbol typeSymbol)
{
builder.AppendLineIndented("#if __ANDROID__");

var registerParamAndroid = $"\"{typeSymbol.GetFullMetadataName().Replace(".", "/")}\"";
var registerParamAndroid = $"\"{typeSymbol.GetFullMetadataName(forRegisterAttributeDotReplacement: '/')}\"";

builder.AppendLineIndented($"[global::Android.Runtime.Register({registerParamAndroid})]");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1353,145 +1353,145 @@ private static VirtualizingLayout GetMonitoringLayout(Size desiredSize, List<Rec
ArrangeLayoutFunc = (finalSize, context) => finalSize
};
}
}

internal partial class TestScrollingSurface : ContentControl, IRepeaterScrollingSurface
{
private bool _isHorizontallyScrollable;
private bool _isVerticallyScrollable;
private ConfigurationChangedEventHandler _configurationChangedTokenTable;

public bool InMeasure { get; set; }
public bool InArrange { get; set; }
public bool InPostArrange { get; private set; }
private partial class TestScrollingSurface : ContentControl, IRepeaterScrollingSurface
{
private bool _isHorizontallyScrollable;
private bool _isVerticallyScrollable;
private ConfigurationChangedEventHandler _configurationChangedTokenTable;

public Action ConfigurationChangedAddFunc { get; set; }
public Action ConfigurationChangedRemoveFunc { get; set; }
public bool InMeasure { get; set; }
public bool InArrange { get; set; }
public bool InPostArrange { get; private set; }

public Action<UIElement> RegisterAnchorCandidateFunc { get; set; }
public Action<UIElement> UnregisterAnchorCandidateFunc { get; set; }
public Func<UIElement, Rect> GetRelativeViewportFunc { get; set; }
public Action ConfigurationChangedAddFunc { get; set; }
public Action ConfigurationChangedRemoveFunc { get; set; }

public UIElement AnchorElement { get; set; }
public Action<UIElement> RegisterAnchorCandidateFunc { get; set; }
public Action<UIElement> UnregisterAnchorCandidateFunc { get; set; }
public Func<UIElement, Rect> GetRelativeViewportFunc { get; set; }

public bool IsHorizontallyScrollable
{
get { return _isHorizontallyScrollable; }
set
{
_isHorizontallyScrollable = value;
RaiseConfigurationChanged();
InvalidateMeasure();
}
}
public UIElement AnchorElement { get; set; }

public bool IsVerticallyScrollable
{
get { return _isVerticallyScrollable; }
set
public bool IsHorizontallyScrollable
{
_isVerticallyScrollable = value;
RaiseConfigurationChanged();
InvalidateMeasure();
get { return _isHorizontallyScrollable; }
set
{
_isHorizontallyScrollable = value;
RaiseConfigurationChanged();
InvalidateMeasure();
}
}
}

public event ConfigurationChangedEventHandler ConfigurationChanged
{
add
public bool IsVerticallyScrollable
{
if (ConfigurationChangedAddFunc != null)
get { return _isVerticallyScrollable; }
set
{
ConfigurationChangedAddFunc();
_isVerticallyScrollable = value;
RaiseConfigurationChanged();
InvalidateMeasure();
}

_configurationChangedTokenTable += value;
}
remove

public event ConfigurationChangedEventHandler ConfigurationChanged
{
if (ConfigurationChangedRemoveFunc != null)
add
{
ConfigurationChangedRemoveFunc();
if (ConfigurationChangedAddFunc != null)
{
ConfigurationChangedAddFunc();
}

_configurationChangedTokenTable += value;
}
remove
{
if (ConfigurationChangedRemoveFunc != null)
{
ConfigurationChangedRemoveFunc();
}

_configurationChangedTokenTable -= value;
_configurationChangedTokenTable -= value;
}
}
}
public event PostArrangeEventHandler PostArrange;
public event PostArrangeEventHandler PostArrange;
#pragma warning disable CS0067
// Warning CS0067: The event 'ViewportTests.TestScrollingSurface.ViewportChanged' is never used.
public event ViewportChangedEventHandler ViewportChanged;
// Warning CS0067: The event 'ViewportTests.TestScrollingSurface.ViewportChanged' is never used.
public event ViewportChangedEventHandler ViewportChanged;
#pragma warning restore CS0067

public void RegisterAnchorCandidate(UIElement element)
{
RegisterAnchorCandidateFunc(element);
}
public void RegisterAnchorCandidate(UIElement element)
{
RegisterAnchorCandidateFunc(element);
}

public void UnregisterAnchorCandidate(UIElement element)
{
UnregisterAnchorCandidateFunc(element);
}
public void UnregisterAnchorCandidate(UIElement element)
{
UnregisterAnchorCandidateFunc(element);
}

public Rect GetRelativeViewport(UIElement child)
{
return GetRelativeViewportFunc(child);
}
public Rect GetRelativeViewport(UIElement child)
{
return GetRelativeViewportFunc(child);
}

protected override Size MeasureOverride(Size availableSize)
{
InMeasure = true;
var result = base.MeasureOverride(availableSize);
InMeasure = false;
return result;
}
protected override Size MeasureOverride(Size availableSize)
{
InMeasure = true;
var result = base.MeasureOverride(availableSize);
InMeasure = false;
return result;
}

protected override Size ArrangeOverride(Size finalSize)
{
InArrange = true;
protected override Size ArrangeOverride(Size finalSize)
{
InArrange = true;

var result = base.ArrangeOverride(finalSize);
var result = base.ArrangeOverride(finalSize);

InArrange = false;
InPostArrange = true;
InArrange = false;
InPostArrange = true;

if (PostArrange != null)
{
PostArrange(this);
}
if (PostArrange != null)
{
PostArrange(this);
}

InPostArrange = false;
InPostArrange = false;

return result;
}
return result;
}

private void RaiseConfigurationChanged()
{
_configurationChangedTokenTable?.Invoke(this);
private void RaiseConfigurationChanged()
{
_configurationChangedTokenTable?.Invoke(this);
}
}
}

internal partial class TestStackLayout : StackLayout
{
public UIElement SuggestedAnchor { get; private set; }

protected internal override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize)
private partial class TestStackLayout : StackLayout
{
var anchorIndex = context.RecommendedAnchorIndex;
SuggestedAnchor = anchorIndex < 0 ? null : context.GetOrCreateElementAt(anchorIndex);
return base.MeasureOverride(context, availableSize);
}
}
public UIElement SuggestedAnchor { get; private set; }

internal partial class TestGridLayout : UniformGridLayout
{
public UIElement SuggestedAnchor { get; private set; }
protected internal override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize)
{
var anchorIndex = context.RecommendedAnchorIndex;
SuggestedAnchor = anchorIndex < 0 ? null : context.GetOrCreateElementAt(anchorIndex);
return base.MeasureOverride(context, availableSize);
}
}

protected internal override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize)
private partial class TestGridLayout : UniformGridLayout
{
var anchorIndex = context.RecommendedAnchorIndex;
SuggestedAnchor = anchorIndex < 0 ? null : context.GetOrCreateElementAt(anchorIndex);
return base.MeasureOverride(context, availableSize);
public UIElement SuggestedAnchor { get; private set; }

protected internal override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize)
{
var anchorIndex = context.RecommendedAnchorIndex;
SuggestedAnchor = anchorIndex < 0 ? null : context.GetOrCreateElementAt(anchorIndex);
return base.MeasureOverride(context, availableSize);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Android.Content;

namespace Uno.UI.RuntimeTests.Tests.NativeCtorGeneratorTests
{
public partial class GenericClass<T1, T2> : Android.Views.View
{
public GenericClass(Context context) : base(context) { }
public GenericClass(Context context, Android.Util.IAttributeSet attributeSet) : base(context, attributeSet) { }

public partial class NestedGenericClass<T3> : Android.Views.View
{
public NestedGenericClass(Context context) : base(context) { }
public NestedGenericClass(Context context, Android.Util.IAttributeSet attributeSet) : base(context, attributeSet) { }
}
}
}

public partial class GenericClassGlobalNamespace<T1, T2> : Android.Views.View
{
public GenericClassGlobalNamespace(Context context) : base(context) { }
public GenericClassGlobalNamespace(Context context, Android.Util.IAttributeSet attributeSet) : base(context, attributeSet) { }

public partial class NestedGenericClassGlobalNamespace<T3> : Android.Views.View
{
public NestedGenericClassGlobalNamespace(Context context) : base(context) { }
public NestedGenericClassGlobalNamespace(Context context, Android.Util.IAttributeSet attributeSet) : base(context, attributeSet) { }
}
}
Loading

0 comments on commit 78790e7

Please sign in to comment.