Skip to content
Merged
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
7 changes: 7 additions & 0 deletions eng/common/core-templates/job/publish-build-assets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ parameters:

repositoryAlias: self

officialBuildId: ''

jobs:
- job: Asset_Registry_Publish

Expand All @@ -62,6 +64,11 @@ jobs:
value: false
# unconditional - needed for logs publishing (redactor tool version)
- template: /eng/common/core-templates/post-build/common-variables.yml
- name: OfficialBuildId
${{ if ne(parameters.officialBuildId, '') }}:
value: ${{ parameters.officialBuildId }}
${{ else }}:
value: $(Build.BuildNumber)

pool:
# We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com)
Expand Down
2 changes: 2 additions & 0 deletions eng/common/core-templates/jobs/jobs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ parameters:
artifacts: {}
is1ESPipeline: ''
repositoryAlias: self
officialBuildId: ''

# Internal resources (telemetry, microbuild) can only be accessed from non-public projects,
# and some (Microbuild) should only be applied to non-PR cases for internal builds.
Expand Down Expand Up @@ -116,3 +117,4 @@ jobs:
artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }}
signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }}
repositoryAlias: ${{ parameters.repositoryAlias }}
officialBuildId: ${{ parameters.officialBuildId }}
2 changes: 1 addition & 1 deletion mk/xamarin.mk
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ endif

# Available versions can be seen here:
# https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/Microsoft.Tools.Mlaunch/versions
MLAUNCH_NUGET_VERSION=1.1.72
MLAUNCH_NUGET_VERSION=1.1.79

define CheckVersionTemplate
check-$(1)::
Expand Down
11 changes: 7 additions & 4 deletions src/Foundation/NSUrlSessionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -940,13 +940,16 @@ void DidReceiveResponseImpl (NSUrlSession session, NSUrlSessionDataTask dataTask
httpResponse.RequestMessage.RequestUri = absoluteUri;

foreach (var v in urlResponse.AllHeaderFields) {
var key = v.Key?.ToString ();
var value = v.Value?.ToString ();

// NB: Cocoa trolling us so hard by giving us back dummy dictionary entries
if (v.Key is null || v.Value is null) continue;
if (key is null || value is null) continue;
// NSUrlSession tries to be smart with cookies, we will not use the raw value but the ones provided by the cookie storage
if (v.Key.ToString () == SetCookie) continue;
if (key == SetCookie) continue;

httpResponse.Headers.TryAddWithoutValidation (v.Key.ToString (), v.Value.ToString ());
httpResponse.Content.Headers.TryAddWithoutValidation (v.Key.ToString (), v.Value.ToString ());
httpResponse.Headers.TryAddWithoutValidation (key, value);
httpResponse.Content.Headers.TryAddWithoutValidation (key, value);
}

// it might be confusing that we are not using the managed CookieStore here, this is ONLY for those cookies that have been retrieved from
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
Expand Down Expand Up @@ -32,10 +33,11 @@ public bool Equals ((string, TypeInfo []) x, (string, TypeInfo []) y)
/// </summary>
public int GetHashCode ((string, TypeInfo []) obj)
{
int hash = obj.Item1.GetHashCode ();
var hash = new HashCode ();
hash.Add (obj.Item1.GetHashCode ());
foreach (var t in obj.Item2)
hash = hash * 31 + (t.GetHashCode ());
return hash;
hash.Add (t.GetHashCode ());
return hash.ToHashCode ();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ public static ImmutableArray<AttributeCodeChange> From (SyntaxList<AttributeList
var bucket = ImmutableArray.CreateBuilder<AttributeCodeChange> ();
foreach (AttributeListSyntax attributeListSyntax in attributes) {
foreach (AttributeSyntax attributeSyntax in attributeListSyntax.Attributes) {
var x = semanticModel.GetSymbolInfo (attributeSyntax);
if (semanticModel.GetSymbolInfo (attributeSyntax).Symbol is not IMethodSymbol attributeSymbol)
continue; // if we can't get the symbol, ignore it
var name = attributeSymbol.ContainingType.ToDisplayString ();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,12 +283,14 @@ internal Binding (BindingInfo bindingInfo, string name, ImmutableArray<string> @
context.SemanticModel.GetSymbolData (
declaration: enumDeclaration,
bindingType: BindingType.SmartEnum,
context: context,
name: out name,
typeInfo: out typeInfo,
baseClass: out baseClass,
interfaces: out interfaces,
outerClasses: out outerClasses,
namespaces: out namespaces,
protocolConstructors: out _, // no constructors in enums
symbolAvailability: out availability,
bindingInfo: out bindingInfo);
FullyQualifiedSymbol = enumDeclaration.GetFullyQualifiedIdentifier (context.SemanticModel);
Expand Down Expand Up @@ -349,12 +351,14 @@ internal Binding (BindingInfo bindingInfo, string name, ImmutableArray<string> @
context.SemanticModel.GetSymbolData (
declaration: classDeclaration,
bindingType: classDeclaration.GetBindingType (context.SemanticModel),
context: context,
name: out name,
baseClass: out baseClass,
typeInfo: out typeInfo,
interfaces: out interfaces,
outerClasses: out outerClasses,
namespaces: out namespaces,
protocolConstructors: out protocolConstructors,
symbolAvailability: out availability,
bindingInfo: out bindingInfo);
FullyQualifiedSymbol = classDeclaration.GetFullyQualifiedIdentifier (context.SemanticModel);
Expand Down Expand Up @@ -393,12 +397,14 @@ internal Binding (BindingInfo bindingInfo, string name, ImmutableArray<string> @
context.SemanticModel.GetSymbolData (
declaration: interfaceDeclaration,
bindingType: BindingType.Protocol,
context: context,
name: out name,
typeInfo: out typeInfo,
baseClass: out baseClass,
interfaces: out interfaces,
outerClasses: out outerClasses,
namespaces: out namespaces,
protocolConstructors: out _, // ingored in interfaces
symbolAvailability: out availability,
bindingInfo: out bindingInfo);
FullyQualifiedSymbol = interfaceDeclaration.GetFullyQualifiedIdentifier (context.SemanticModel);
Expand Down Expand Up @@ -480,6 +486,8 @@ public override string ToString ()
sb.AppendJoin (", ", EnumMembers);
sb.Append ("], Constructors: [");
sb.AppendJoin (", ", Constructors);
sb.Append ("], ProtocolConstructors: [");
sb.AppendJoin (", ", ProtocolConstructors);
sb.Append ("], Properties: [");
sb.AppendJoin (", ", Properties);
sb.Append ("], ParentProtocolProperties: [");
Expand Down
25 changes: 25 additions & 0 deletions src/rgen/Microsoft.Macios.Generator/DataModel/Binding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,31 @@ public ImmutableArray<Constructor> Constructors {
/// </summary>
public ImmutableArray<string> ConstructorSelectors => [.. constructorIndex.Keys];

readonly Dictionary<string, int> protocolConstructorIndex = new ();
readonly ImmutableArray<Constructor> protocolConstructors = [];

/// <summary>
/// Gets the constructors inherited from parent protocols.
/// </summary>
public ImmutableArray<Constructor> ProtocolConstructors {
get => protocolConstructors;
init {
protocolConstructors = value;
// populate the constructor index for fast lookup using the symbol name
for (var index = 0; index < protocolConstructors.Length; index++) {
var constructor = protocolConstructors [index];
if (constructor.Selector is null)
continue;
protocolConstructorIndex [constructor.Selector] = index;
}
}
}

/// <summary>
/// Returns all the selectors for the constructors.
/// </summary>
public ImmutableArray<string> ProtocolConstructorSelectors => [.. protocolConstructorIndex.Keys];

readonly Dictionary<string, int> eventsIndex = new ();
readonly ImmutableArray<Event> events = [];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,15 @@ readonly partial struct Constructor {
public Location? Location { get; init; }

/// <summary>
/// True if the cosntructor was marked to skip its registration.
/// True if the constructor was marked to skip its registration.
/// </summary>
public bool SkipRegistration => ExportMethodData.Flags.HasFlag (ObjCBindings.Constructor.SkipRegistration);

/// <summary>
/// True if the constructor is thread safe.
/// </summary>
public bool IsThreadSafe => ExportMethodData.Flags.HasFlag (ObjCBindings.Constructor.IsThreadSafe);

public Constructor (string type,
SymbolAvailability symbolAvailability,
ExportData<ObjCBindings.Constructor> exportData,
Expand Down
5 changes: 5 additions & 0 deletions src/rgen/Microsoft.Macios.Generator/DataModel/Constructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ namespace Microsoft.Macios.Generator.DataModel;
/// </summary>
public bool IsNullOrDefault => State == StructState.Default;

/// <summary>
/// Gets or sets a value indicating whether the constructor comes from a protocol factory method.
/// </summary>
public bool IsProtocolConstructor { get; init; }

/// <summary>
/// Type name that owns the constructor.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ public static bool TryCreate (MethodDeclarationSyntax declaration, RootContext c
change = new (
type: method.ContainingSymbol.ToDisplayString ().Trim (), // we want the full name
name: method.Name,
returnType: new TypeInfo (method.ReturnType, context),
returnType: new TypeInfo (method.ReturnType, context, includeEvents: false),
symbolAvailability: method.GetSupportedPlatforms (),
exportMethodData: exportData,
attributes: attributes,
Expand Down Expand Up @@ -315,7 +315,7 @@ .. Modifiers.Where (m =>
/// A new <see cref="Constructor"/> instance if the method is a factory method;
/// otherwise, an uninitialized <see cref="Constructor"/> instance.
/// </returns>
public Constructor ToConstructor (TypeInfo targetClass)
public Constructor ToConstructor (string targetClass)
{
// if the method is not a factory, we cannot convert it to a constructor so we will return the default value
// which is an uninitialized instance
Expand All @@ -325,13 +325,17 @@ public Constructor ToConstructor (TypeInfo targetClass)
// we need to create a constructor with the same modifiers, parameters and the availability of the method
// since there is no guarantee that the target class has the same availability as the method
return new (
type: targetClass.Name,
exportData: new (ExportMethodData.Selector),
type: targetClass,
exportData: IsThreadSafe
? new (ExportMethodData.Selector) { Flags = ObjCBindings.Constructor.IsThreadSafe }
: new (ExportMethodData.Selector),
symbolAvailability: SymbolAvailability,
attributes: [], // we do not really care about the attributes on the constructor that is going to be inlined
modifiers: modifiers,
parameters: Parameters
);
) {
IsProtocolConstructor = true
};
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,16 @@ static ImmutableArray<EventInfo> GetInterfaceEvents (string typeNamespace, IType
return parentMethodsBucket.ToImmutable ();
}

internal TypeInfo (ITypeSymbol symbol, RootContext context) : this (symbol)
/// <summary>
/// Initializes a new instance of the <see cref="TypeInfo"/> struct from a type symbol, with additional generator-specific information.
/// </summary>
/// <param name="symbol">The type symbol to create the <see cref="TypeInfo"/> from.</param>
/// <param name="context">The root context for compilation information.</param>
/// <param name="includeEvents">A flag to indicate whether to include event information for protocols.</param>
internal TypeInfo (ITypeSymbol symbol, RootContext context, bool includeEvents = true) : this (symbol)
{
NeedsStret = symbol.NeedsStret (context.Compilation);
if (symbol.IsProtocol ()) {
if (symbol.IsProtocol () && includeEvents) {
Events = GetInterfaceEvents (string.Join ('.', Namespace), symbol, context);
}
}
Expand Down
62 changes: 56 additions & 6 deletions src/rgen/Microsoft.Macios.Generator/Emitters/ClassEmitter.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
Expand All @@ -18,9 +19,31 @@
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using Constructor = ObjCBindings.Constructor;
using Property = Microsoft.Macios.Generator.DataModel.Property;
using TypeInfo = Microsoft.Macios.Generator.DataModel.TypeInfo;

namespace Microsoft.Macios.Generator.Emitters;

file class ConstructorParameterComparer : IEqualityComparer<ImmutableArray<TypeInfo>> {
/// <summary>
/// Determines equality by requiring the same method name and identical ordered parameter type sequence.
/// </summary>
public bool Equals (ImmutableArray<TypeInfo> x, ImmutableArray<TypeInfo> y)
{
return x.SequenceEqual (y);
}

/// <summary>
/// Computes a hash code combining the method name and ordered parameter types.
/// </summary>
public int GetHashCode (ImmutableArray<TypeInfo> obj)
{
var hash = new HashCode ();
foreach (var t in obj)
hash.Add (t);
return hash.ToHashCode ();
}
}

/// <summary>
/// Emitter for Objective-C classes.
/// </summary>
Expand Down Expand Up @@ -54,9 +77,30 @@ public string GetSymbolName (in Binding binding)
/// <param name="classBlock">Current class block.</param>
void EmitConstructors (in BindingContext context, TabbedWriter<StringWriter> classBlock)
{
// merge the constructors and the protocol constructors for the current class, use a dict to avoid duplicates
var allConstructors = new Dictionary<ImmutableArray<TypeInfo>, DataModel.Constructor> (new ConstructorParameterComparer ());
foreach (var constructor in context.Changes.Constructors) {
if (constructor.Selector is null)
continue;
var key = constructor.Parameters.Select (x => x.Type).ToImmutableArray ();
allConstructors.TryAdd (key, constructor);
}

foreach (var constructor in context.Changes.ProtocolConstructors) {
if (constructor.Selector is null)
continue;
var key = constructor.Parameters.Select (x => x.Type).ToImmutableArray ();
allConstructors.TryAdd (key, constructor);
}

// create the ui thread check to be used in the constructors that come from a protocol factory method
var uiThreadCheck = (context.NeedsThreadChecks)
? EnsureUiThread (context.RootContext.CurrentPlatform)
: null;

// When dealing with constructors we cannot sort them by name because the name is always the same as the class
// instead we will sort them by the selector name so that we will always generate the constructors in the same order
foreach (var constructor in context.Changes.Constructors.OrderBy (c => c.ExportMethodData.Selector)) {
foreach (var constructor in allConstructors.Values.OrderBy (c => c.ExportMethodData.Selector)) {
classBlock.AppendMemberAvailability (constructor.SymbolAvailability);
classBlock.AppendGeneratedCodeAttribute (optimizable: true);
if (constructor.ExportMethodData.Flags.HasFlag (Constructor.DesignatedInitializer)) {
Expand All @@ -68,6 +112,12 @@ void EmitConstructors (in BindingContext context, TabbedWriter<StringWriter> cla
}

using (var constructorBlock = classBlock.CreateBlock (constructor.ToDeclaration (withBaseNSFlag: true).ToString (), block: true)) {
if (uiThreadCheck is not null && constructor is { IsProtocolConstructor: true, IsThreadSafe: false }) {
// if we are dealing with a protocol constructor, we need to ensure we are on the UI thread, this
// happens for example with NSCoding in ui elements.
constructorBlock.Write (uiThreadCheck.ToString ());
constructorBlock.WriteLine ();
}
// retrieve the method invocation via the factory, this will generate the necessary arguments
// transformations and the invocation
var invocations = GetInvocations (constructor);
Expand Down Expand Up @@ -122,12 +172,12 @@ void EmitNotifications (in ImmutableArray<Property> properties, TabbedWriter<Str
: notification.ExportFieldData.FieldData.Type;
// use the raw writer which makes it easier to read in this case
notificationClass.WriteRaw (
@$"public static {NSObject} {name} ({EventHandler}<{eventType}> handler)
@$"public static {NSObject} {name} ({BindingSyntaxFactory.EventHandler}<{eventType}> handler)
{{
return {notificationCenter}.AddObserver ({notification.Name}, notification => handler (null, new {eventType} (notification)));
}}

public static NSObject {name} ({NSObject} objectToObserve, {EventHandler}<{eventType}> handler)
public static NSObject {name} ({NSObject} objectToObserve, {BindingSyntaxFactory.EventHandler}<{eventType}> handler)
{{
return {notificationCenter}.AddObserver ({notification.Name}, notification => handler (null, new {eventType} (notification)), objectToObserve);
}}
Expand Down Expand Up @@ -193,8 +243,8 @@ void EmitEvents (in BindingContext bindingContext, in ImmutableArray<Property> d
foreach (var eventInfo in property.ExportPropertyData.StrongDelegateType.Events) {
// create the event args type name
var eventHandler = eventInfo.EventArgsType is null
? EventHandler.ToString ()
: $"{EventHandler}<{eventInfo.EventArgsType}>";
? BindingSyntaxFactory.EventHandler.ToString ()
: $"{BindingSyntaxFactory.EventHandler}<{eventInfo.EventArgsType}>";
using (var eventBlock =
classBlock.CreateBlock ($"public event {eventHandler} {eventInfo.Name}", true)) {
eventBlock.WriteLine ($"add {{ {ensureMethod} ()!.{eventInfo.Name.Decapitalize ()} += value; }}");
Expand Down
Loading
Loading